Blind Geeks dot org

Basic Audio Programming

Part I: Playing Audio

 

 

DISCLAIMER:

 

This lesson series is intended for experienced computer programming professionals.  Please read the following before continuing.

 

 

  It is assumed that student has successfully completed courses or lessons in at least one computer language.

  While languages such as Visual Basic, C#, Flex and Actionscript are demonstrated in this series, this is not a series instructing the student in programming in these languages.  This is a series in Audio Programming.

 

 

 

Introduction:

 

For about 5 years, I have been planning on doing an audio programming tutorial since audio is my real love (don’t tell my wife please).

 

When I was only 10 years old, I decided to become a radio announcer when I grew up.  I still haven’t grown up but when I was 11 years old, I pieced together a “studio” from two old record players, a 1955 RCA amplifier, a reel to reel deck which my blind mother got for listening to book from the school for the blind and a 1945 RCA microphone that the radio station a mile away from my home threw in their dumpster one night.

 

I lived in a town named Three Rivers in Michigan when I was 10, just a mile from WLKM AM/FM Radio Stations.  The morning D.J. and Program Director, Dennis Rumsey, allowed me to visit him when he was on the air and to play in the production studio.  He taught me basic things like how to “cue up” a record.  I was hooked after just a few visits.

 

At age 15, I became a professional radio announcer when I was hired at WAOP-FM in Otsego, Michigan.

 

By the age of 20, I was one of America’s youngest Program Directors of a market with a population of more than 300,000 people.  I ended up in Mobile, Alabama where I burned out on the radio business and enrolled in college where I got into computers.

 

By age 25, I had left the radio business all together but still had music and radio in my blood.

 

Later, computers became a way that I could mix my passion for music, radio and computers into one.  While in college, I developed a radio music rotation system in IBM Basic that we used at the radio station I worked at, WABB-AM.  Then in 1988, I had helped a friend of mine develop a music rotation system (play list) for professional radio written in Pascal.  In 1990, that system was used by more than 35 radio stations in the United States.  In 1997, I developed a system of my own in Visual Basic 6.  In 2004, I improved on it and rewrote in VB.NET and renamed it to Crescendo Music/Program Director.  As a means to promote this system, I put up an internet radio station that year called The Shadow.  It is still streaming today.

 

In 2005, I went to work for an audio software company where I helped develop modifications to an application called The Singulator.

 

My passion for audio continued after leaving this position and continues today.  I take great joy in offering this series on Audio Programming.  I hope you enjoy it as much as I enjoyed doing it.

 

 

1.                    The History of Digital Audio.

 

In 1981, the concept of automating audio playback at a radio station consisted of two to four reel to reel decks for music playback, two to four machines called Cart Carousels for  commercial (spots) and jingle/promotion playback and a main “brain” which was very simplistic compared to even the computers of the mid-1980’s.

 

This entire system took up the entire wall of a radio station control room.  The programming of this monster was done solely in a sequence of numbers which were actual decimal.  After the “programmer” typed in these numbers and pressed the ENTER key, the computer translated these numbers into binary.

 

These numbers were generated by a computer company based on the sequence of the playback device and were sent to the radio station in a folder each time a new reel tape was sent.  This usually happened once per week.

 

The “programmer” was actually a radio station employee who did not really write any code at all but merely input these numbers into the computer.

 

I became one of these programmers after working at the radio station for a few months.  The AM station, WOAM-AM was automated using this computer from sunrise to sunset then the FM Station, WAOP-FM used the automation system from 7pm to 6am.

 

I learned how to program the automation system to play the music sequentially or to play certain cuts from a reel tape.

 

At the end of each song on the reel tape was an inaudible tone that the computer detected and interpreted to trigger the start of the next reel deck or cart deck, depending on whether music would be played next or if it were time for a commercial or jingle to play.  These were called Events.  If you do any type of programming today, you recognize this word from programming.

 

In 1983, when I worked at a small town radio station that had a 3,000 watt FM and a small 250 watt AM station in Hastings, Michigan, I was responsible for “programming” the AM automation which was programmed in BASIC on a Timex Sinclair PC which used a cassette tape recorder made by Radio Shack to store its “programs”.  The program had been written by the radio station’s Chief Engineer who was a computer hobbyist at the time.  It was miles more advanced than the system which cost more than $100k in 1981 but was much cheaper and more reliable.  It controller a series of cassette machines which played back songs and commercials.  It wasn’t as fancy as the $100,000 system but it did a better job.

 

That was when I yearned for a way to develop a way to automate a station myself.  In the 1990s, I started developing a system which used a series of CD decks but technology moved faster than I could.

 

By 1997, radio stations began using personal computers for on-air playback using a system called Audio Vault which played Wave files stored on the computer’s hard drive.  With smaller hard drives and speed issues back then, the bigger and faster hard drives called SCSI (pronounced Scuzzy) were very expensive so only bigger radio stations could afford these systems.  To give you an example, the average 4 minute song took up about 45 megabytes of disk space.  The biggest hard drive that they had back then was about 60 gig in size, so about 150 songs could fit on one hard drive.  Usually, the system had 1 to 3 SCSI drives chained to a single controller.  The more expensive systems had up to 6 hard drives.

 

Around the year 2000, the MPEG-2 Layer III audio format (MP3) became popular but it took a year or so for radio stations to adopt this format due to radio and music industry audio “experts” complaining that the quality of the MP3 format was not good enough for on-air use.

 

The radio station owners and management won this argument because the MP3 format was much cheaper to use in systems than the current WAVE format and less expensive systems could be put into place instead of the expensive SCSI systems.  With the MP3 format, more files could be stored and played on IDE hard drives with no performance issues and they were much cheaper.

 

In about 1998, CD burners became popular and consumers began buying them and dubbing their old records and tapes onto the computer then burning them to CD.  The format used at the time was Wave but that changed when MP3 became popular.  Shortly after, CD burners allowed you to store up to 100 songs in MP3 format on a CD and play back on consumer CD decks.

 

In 1999, online music sharing sites such as Napster, allowed computer users to connect to one another and share their MP3 files.  When the record industry made Napster go under, ten more such sites sprung up overnight.  Now, there are pay subscription services such as iTunes, BearShare and others that either allow you to pay a monthly subscription fee or pay a small fee per download.  The MP3 file format is still the favored after 10 years even though formats such as Ogg have become popular over the years.

 

For the purposes of this series, we will be using the Wave and MP3 formats.  All music files including with the examples projects have been recorded in low quality formats to avoid prosecution from various organizations.  You are free to record and use your own music files to use in these examples.

 

We are going to introduce audio technology in this series from the past to the present in that order.  We will begin with Windows Application Audio programming then go to Web Application Audio Programming then end with an advanced audio application.  We start by showing how to play audio in this part of the series.

 

Section 1: Windows Application Audio Programming

 

2.                    Low-Level MCI Audio.

 

MCI stands for Media Control Interface and is founded on the C++ language but we will use C# to demonstrate playing Wave files and Visual Basic (Using Visual Studio 2010) to do our demonstration of playing MP3 files in this chapter.

 

In this chapter, we will write a small Windows application in C# to demonstrate playing wave files using the low-level MCI technology.  We will then write a pretty sophisticated Visual Basic application that is a mock Radio Station DJ program to demonstrate playing MP3 files with the high-level mciSendString command.

 

If you do not have Visual Studio 2010, this code should work in Visual Studio 2008 as well.

 

Low-Level MCI Playback Technology

 

The native digital audio format is the raw or Wave data format.  When audio CDs are ripped, the audio is taken from the CD and placed into this Wave format.  It is a very high quality format but takes up much disk space.

 

The Windows operating system uses the wave format for its Windows Sounds.  So does many computer games even today.

 

MCI was founded using the Visual C++ language and many of the existing MCI code is done in C++.  However, over the years, developers have translated the C++ definitions and code to C#, VB and even Java.  But, as time goes by and the technology is replaced by newer technology, it is getting harder to find sample code or any help from forums or even from Microsoft.  Microsoft officially no longer supports the MCI technology as they replaced it with DirectX.

 

In 2005, I helped develop and support upgrades to a C++ application called Singulator that was written using low-level MCI.  That program was originally written in 1998 and has now faded and is no longer supported by the company full-time.  I haven’t worked with this company since 2008 but my name is still listed on the web page for support contact.  This is an example of how the MCI technology has lost ground to the point where companies no longer support their MCI products.

 

This is caused mostly by the Wave format no longer being used and the audio system overhaul that Microsoft did when they developed Vista.  Now, with Windows 7, the old MCI applications that would not function under Vista will function in XP compatibility mode of Windows 7 but when they either did not work or had issues under Vista, many customers uninstalled the product and never went back to it.

 

During the development of the sample project for this chapter, I had issues with Windows Vista.  Unfortunately, I did not discover this until I was almost finished with the project.  So, this made me have to re-think a few areas of the demonstration project.  One method, in particular, for getting the song length caused run-time errors under Vista but worked flawlessly under Windows XP.  So, I was forced to read the MP3 ID3 tag to get the length of the song.  This caused about a day of delay in my development process.

 

There are two ways to play audio in MCI.

 

1.                 Low-Level process using WaveOut methods.

2.                 High-Level process using the mciSendString method.

 

The low-level WaveOut process can only natively play wave files.  It is possible to play other audio formats but you must first build methods to decode the audio from that format and place it into the wave format before calling the methods to play the audio.  With newer technologies available that allow you to do the same thing with less work, using low-level MCI to play other formats just doesn’t seem feasible unless you needed to process the audio in some way.

 

For the purposes of this first demonstration, we are going to write a very simple application that loads and plays a wave file.  The application will be simple but once you see how much code it takes to do this, it may not seem so simple after all.  That is why we won’t be spending much time on low-level MCI audio.

 

Project Setup & Visuals

 

1.                 Start Visual Studio 2010.

2.                 Create a new C# Windows Forms Project named BGLowLevelMCI.

3.                 When the project loads, open Solution Explorer and rename Form1.cs to Main.cs.

4.                 When prompted to rename all references, activate the Yes button.

5.                 Open Main.cs in Design View.

 

Just a note about visuals here.  We are about to design our player form.  I’ve already designed it for you but will instruct you on making some visual improvements to the normal bland Windows form.

 

I like to make buttons larger than the default and make them stand out with color.  Just because you may be blind or visually impaired does not mean that, with a little sighted assistance, you cannot make your forms more visually pleasing.

 

The form we are about to create will have a shaded label with a black background and a yellow foreground color.  This resembles the 1980’s cassette deck LED displays.  This label will be used to display the song playback status.

 

It will have a menu strip so that we can assign meaningful shortcut keys to the player buttons.

 

And, finally, it will have four very large, colored buttons:  Load Song, Play Song, Pause Song and Stop Song.  I use a blue (Navy) background and white foreground color for the Load Song button but for the rest of the buttons, I use colors that a sighted person would see on a traffic stop light.  Green for Play, Yellow for pause and Red for Stop.

 

After we get the layout done, we will play with these colors a little in our code to make the buttons look differently when they are activated.  This way, the visual person can easily tell the status of the player by looking at the coloring of the buttons.

 

1.                 Resize Main.cs in designer mode to ().

2.                 Add an Open File Dialog control named ofdOpenWave with the following properties:

 

Property

Value

Filter

Wave Audio (*.wav)|*.wav

Title

Open Wave Audio File

 

 

3.                 Drop a label control named lblSongStatus onto the form with the following properties:

 

Property

Value

Backcolor

Black

Forecolor

Yellow

TextAlign

Center

Text

 

Size

 

Location

 

The text property above is blank and is not a typo.

 

4.                 Add a button control named btnLoad with the following properties:

 

Property

Value

Backcolor

Navy

Forecolor

White

Text

Load Song

Font

 

Size

 

Location

 

Enabled

True

 

5.                 Add a button control named btnPlay with the following properties:

 

Property

Value

Backcolor

DarkGreen

Forecolor

DarkGray

Text

Play Song

Font

 

Size

 

Location

 

Enabled

False

 

6.                 Add a button control named btnPause with the following properties:

 

Property

Value

Backcolor

Gold

Forecolor

DarkGray

Text

Pause Song

Font

 

Size

 

Location

 

Enabled

False

 

7.                 Add a button control named btnStop with the following properties:

 

Property

Value

Backcolor

DarkRed

Forecolor

DarkGray

Text

Stop Song

Font

 

Size

 

Location

 

Enabled

False

 

8.                 Add a menustrip control named mnuMain.

9.                 Add menu items to mnuMain with the following properties:

 

Menu Item

Parent Item

Property

Value

mnuFile

 

Text

&File

mnuExit

mnuFile

Text

E&xit

mnuAction

 

Text

&Action

mnuLoadSong

mnuAction

Text

Load Song

mnuLoadSong

mnuAction

ShortcutKeys

F2

mnuPlaySong

mnuAction

Text

Play Song

mnuPlaySong

mnuAction

ShortcutKeys

F3

mnuPauseSong

mnuAction

Text

Pause Song

mnuPauseSong

mnuAction

ShortcutKeys

F4

mnuStopSong

mnuAction

Text

Stop Song

mnuStopSong

mnuAction

ShortcutKeys

F5

 

10.     Save your work.

 

You may have noticed that we set the default state of all but the Load button to enabled = false.  It is good practice not to allow the user to click buttons when the action of those buttons could cause errors.  Naturally, if the user tried to play, stop or pause a song which hasn’t been loaded yet, it may cause a run-time error unless there is validation in the click event handling code.  So, by disabling the buttons when no click actions should be allowed, we reduce the risk of errors.

 

Before we add any audio coding, we are going to make the buttons and menu items activate the way they should.  So, later, all we have to do is add the audio functionality.  This also simplifies the process of debugging.

 

1.                 Open Visual Studio 2010.

2.                 Open the BGLowLevelMCI project.

3.                 Create Click Event Handlers for btnLoad, btnPlay, btnPause and btnStop.  (In C#, this is done from the properties window or by double-clicking on the button in the designer.

4.                 Create Click Event Handlers for mnuExit, mnuLoadSong, mnuPlaySong, mnuPauseSong and mnuStopSong. (Hint: If the code in the menu click event handler is the same as the button click event handler, you may enter the button’s handler as the menu item’s handler in the properties window.  In VB, you would add the control to the Handles clause of the event handler.  Example:  <function signature here> Handles btnPlay.Click, mnuPlaySong.Click.

5.                 Create the following private void functions but do not add code yet:  LoadSong(), PlaySong(), PauseSong(), StopSong() and CloseSong().

6.                 Save Your Work.

 

 

We are now going to add some code that will help us avoid run-time errors and add some visual effect to the program.

 

When the program begins, only one button should be available to the user: btnLoad.  The rest should be disabled.  This includes the menu items as well.  If we tried to play a song before it is loaded, we’d get a run-time error.  So, we will simply disable the Play button until a song is loaded.  The same is true of the Pause and Stop buttons.

 

1.     Add a form load event handler to the form.

2.     In the load event handler, type the following code:

 

            btnPlay.Enabled = false;

            mnuPlaySong.Enabled = false;

            btnPause.Enabled = false;

            mnuPauseSong.Enabled = false;

            btnStop.Enabled = false;

            mnuStopSong.Enabled = false;

            btnLoad.Enabled = true;

            mnuLoadSong.Enabled = true;

 

The code above disables all buttons and menu items except for btnLoad and mnuLoadSong.

 

3.     In the function LoadSong, type the following code:

 

           DialogResult res = ofdOpenWave.ShowDialog();

            if (res != DialogResult.OK)

            {

                return;

            }

            // load wave file

            strWaveFile = ofdOpenWave.FileName;

 

4.     Put a comment on the next line of code that says //Load Audio File Here.

5.     Skip about three lines before adding the next section of code.  When we are ready to add the audio code, we will add it here.

6.     Type the following code next:

 

           // Set buttons

            lblStatus.Text = ofdOpenWave.FileName + " Loaded.";

            btnPlay.Enabled = true;

            mnuPlaySong.Enabled = true;

            btnPlay.ForeColor = Color.White;

            btnPlay.BackColor = Color.DarkGreen;

            btnPause.Enabled = false;

            mnuPauseSong.Enabled = false;

            btnPause.BackColor = Color.Gold;

            btnPause.ForeColor = Color.DarkGray;

            btnStop.Enabled = false;

            mnuStopSong.Enabled = false;

            btnStop.BackColor = Color.DarkRed;

            btnStop.ForeColor = Color.DarkGray;

 

7.     Save Your Work.

 

In the code above, we activated the Open File Dialog control to allow the user to select the file they wished to load.  If the user clicks Cancel, the file dialog is closed and no file is loaded.  But, if the user selects a file and clicks OK, the button states and colors are changed.

 

The colors of btnLoad never change but the other buttons do change in color as described in this table.

 

Control

BackColor

ForeColor

Condition

btnPlay

DarkGreen

DarkGray

Disabled – No Song Loaded

btnPlay

Green

White

Enabled

btnPlay

Lime (Light Green)

Navy (Dark Blue)

Song Playing

btnPause

Gold

DarkGray

Disabled

btnPause

Gold

Black

Enabled – Song Playing

btnPause

Yellow

Navy

Song Paused

btnStop

DarkRed

DarkGray

Disabled.

btnStop

DarkRed

White

Enabled. Song Playing.

btnStop

Red

White

Song Stopped but still loaded.

 

The colors as they change depending on the state of the Player application resemble the buttons used on 1980’s cart machines, which were tape machines used for commercials at radio stations.

 

1.     Add the following code to the PlaySong() function

 

            //Add Audio Code Here

 

 

            // Set Button States

            btnPlay.BackColor = Color.Lime;

            btnPlay.ForeColor = Color.Navy;

            btnPlay.Enabled = false;

            mnuPlaySong.Enabled = false;

            btnPause.Enabled = true;

            mnuPauseSong.Enabled = true;

            btnPause.ForeColor = Color.Black;

            btnPause.BackColor = Color.Gold;

            btnStop.Enabled = true;

            mnuStopSong.Enabled = true;

            btnStop.ForeColor = Color.White;

            btnStop.BackColor = Color.DarkRed;

            lblStatus.Text = strWaveFile + " Now Playing...";

 

 

We just added a comment and space for the audio code then the next section of the code sets the states and colors of the buttons and menu items.

 

2.     Add the following code to the function named PauseSong():

 

            if (btnPause.BackColor == Color.Gold)

            {

 

 

                // Add Audio Code Here

 

 

                // Set Button States

                btnPlay.BackColor = Color.DarkGreen;

                btnPlay.ForeColor = Color.DarkGray;

                btnPlay.Enabled = false;

                mnuPlaySong.Enabled = false;

                btnPause.Enabled = true;

                mnuPauseSong.Enabled = true;

                btnPause.ForeColor = Color.Navy;

                btnPause.BackColor = Color.Yellow;

                btnStop.Enabled = false;

                mnuStopSong.Enabled = false;

                btnStop.ForeColor = Color.DarkGray;

                btnStop.BackColor = Color.DarkRed;

                lblStatus.Text = strWaveFile + " Paused...";

            }

            else

            {

 

                // Unpause

                //Add Audio Code Here

 

               

                // Set Button States

                btnPlay.BackColor = Color.Lime;

                btnPlay.ForeColor = Color.Navy;

                btnPlay.Enabled = false;

                mnuPlaySong.Enabled = false;

                btnPause.Enabled = true;

                mnuPauseSong.Enabled = true;

                btnPause.ForeColor = Color.Black;

                btnPause.BackColor = Color.Gold;

                btnStop.Enabled = true;

                mnuStopSong.Enabled = true;

                btnStop.ForeColor = Color.White;

                btnStop.BackColor = Color.DarkRed;

                lblStatus.Text = strWaveFile + " Now Playing...";

 

            }

 

In the code above, we checked to see if the button is disabled.  If so, we change its state to enabled and set the colors accordingly.  If the button is enabled and in Paused state as defined by its background color, we set the button states to “unpause” the song.

 

3.     Add the following code to the function named StopSong():

 

            // Add Audio Code Here

 

            // Set Button States

            btnPlay.BackColor = Color.DarkGreen;

            btnPlay.ForeColor = Color.White;

            btnPlay.Enabled = true;

            mnuPlaySong.Enabled = true;

            btnPause.Enabled = false;

            mnuPauseSong.Enabled = false;

            btnPause.ForeColor = Color.DarkGray;

            btnPause.BackColor = Color.Gold;

            btnStop.Enabled = false;

            mnuStopSong.Enabled = false;

            btnStop.ForeColor = Color.DarkGray;

            btnStop.BackColor = Color.Red;

            lblStatus.Text = strWaveFile + " Stopped...";

 

In the code above, we left a few spaces for our audio code and then set the button states so that the song is in “Stopped” mode.

 

4.     Now, add a call to the appropriate function in each of the button’s click event handler.  Do the same for the menu items.  Example:  in btnPlay click event handler, you would call PlaySong().

5.     Save Your Work.

6.     Compile Your Project to make sure there are no errors.  If there are errors, fix them.

7.     Test Your Work.  Make use of the Messagebox.Show or Console.Writeline method to ensure that the button states and colors are changing when they should.

 

Adding The Audio Code

 

I’m not going to go in-depth into the low-level MCI library.  The purpose of this section is to show you what the code looks like and to show you how to load, play, pause and stop audio files using this technology.

 

We will be using three classes that can be found on the Microsoft web site and then calling methods from these classes in our program.

 

These three classes are very complicated and are out of the scope of this lesson.  Since this technology is so old and there are easier alternatives, I do not see the need to teach these techniques in greater detail.

 

You may, however, look at these classes and learn from it independently, if you wish.

 

These classes include data structures for the Wave format as well as the methods we will be using in our program.

 

1.                 Download the chapter solution project from the web site.

2.                 Unzip the project.

3.                 Open Visual Studio 2010.

4.                 Open the BGLowLevelMCI project.

5.                 Open Solution Explorer.

6.                 Select the BGLowLevelMCI project in Solution Explorer and bring up the context menu.

7.                 Add an Existing Item to the project.

8.                 Add these files from the solution project you downloaded from the web site:  WaveOut.cs, WaveNative.cs, WaveStream.cs.

9.                 Build the solution to make sure there are no errors.

 

 

As stated above, we are not going to spend time on the complicated code inside these three classes but will show you how to use these classes to load, play, pause and stop audio.  You may investigate this code on your own, if it interests you.

 

1.     Add a function named CloseSong() and add the following code to it:

 

            StopSong();

            lblStatus.Text = "";

 

2.     Add the following definitions to the top of your form class:

 

        private string strWaveFile = "";

 

        private WaveLib.WaveStream audioStream;

        private WaveLib.WaveOutPlayer waveOutPlayer;

        private WaveLib.WaveFormat waveFormat;

        private WaveLib.WaveNative waveNative;

 

        private long wavePosition = 0;

 

3.     Add the following code to LoadSong() before the code that sets the button states:

 

            // load wave file

            strWaveFile = ofdOpenWave.FileName;

                                try

                                {

                    WaveLib.WaveStream waveStream = new WaveLib.WaveStream(strWaveFile);

                    if (waveStream.Length <= 0)

                        throw new Exception("Invalid WAV file");

                    waveFormat = waveStream.Format;

                    if (waveFormat.wFormatTag != (short)WaveLib.WaveFormats.Pcm && waveFormat.wFormatTag != (short)WaveLib.WaveFormats.Float)

                                                throw new Exception("Only PCM Wave files are supported");

 

                    audioStream = waveStream;

                                }

                                catch(Exception e)

                                {

                                        CloseSong();

                                        MessageBox.Show(e.Message);

                    return;

                                }

                wavePosition = 0;

 

The code above opens a Try…Catch block to trap any errors that occur, opens and loads the selected wave file.

 

4.     Add a function called FillWaveBuffer().  Add the following code to this function:

 

            byte[] b = new byte[size];

            if (audioStream != null)

            {

                int pos = 0;

                while (pos < size)

                {

                    int toget = size - pos;

                    int got = audioStream.Read(b, pos, toget);

                    if (got < toget)

                        audioStream.Position = 0; // loop if the file ends

                    pos += got;

                }

            }

            else

            {

                for (int i = 0; i < b.Length; i++)

                    b[i] = 0;

            }

            System.Runtime.InteropServices.Marshal.Copy(b, 0, data, size);

 

This function loads the wave data from the file into memory.

 

5.     Add the following code to the beginning of PlaySong():

 

            //Add Audio Code Here

            StopSong();

            if (audioStream != null)

            {

                audioStream.Position = 0;

                wavePosition = 0;

                waveOutPlayer = new WaveLib.WaveOutPlayer(-1, waveFormat, 16384, 3, new WaveLib.BufferFillEventHandler(FillWaveBuffer));

            }

 

The code above calls StopSong() to stop and close any currently playing songs, sets the position of the play head to zero (beginning of the song) then starts playback from the memory buffer.

 

6.     Add the following code to StopSong before the code where the button states are set:

 

            // Add Audio Code Here

            if (waveOutPlayer != null)

            {

                try

                {

                    waveOutPlayer.Dispose();

                }

                finally

                {

                    waveOutPlayer = null;

                }

                if (btnStop.Enabled == true)

                {

                    wavePosition = 0;

                }

            }

 

 

This code disposes of the player object, effectively stopping and closing the wave file/buffer then sets the variable we use for the playback head position to zero.

 

7.     Modify the PauseSong() code so that it looks like the following:

 

            if (btnPause.BackColor == Color.Gold)

            {

               

                // Add Audio Code Here

                waveOutPlayer.Pause();

 

 

                // Set Button States

                btnPlay.BackColor = Color.DarkGreen;

                btnPlay.ForeColor = Color.DarkGray;

                btnPlay.Enabled = false;

                mnuPlaySong.Enabled = false;

                btnPause.Enabled = true;

                mnuPauseSong.Enabled = true;

                btnPause.ForeColor = Color.Navy;

                btnPause.BackColor = Color.Yellow;

                btnStop.Enabled = false;

                mnuStopSong.Enabled = false;

                btnStop.ForeColor = Color.DarkGray;

                btnStop.BackColor = Color.DarkRed;

                lblStatus.Text = strWaveFile + " Paused...";

            }

            else

            {

                // Unpause

                //Add Audio Code Here

                waveOutPlayer.Resume();

               

                // Set Button States

                btnPlay.BackColor = Color.Lime;

                btnPlay.ForeColor = Color.Navy;

                btnPlay.Enabled = false;

                mnuPlaySong.Enabled = false;

                btnPause.Enabled = true;

                mnuPauseSong.Enabled = true;

                btnPause.ForeColor = Color.Black;

                btnPause.BackColor = Color.Gold;

                btnStop.Enabled = true;

                mnuStopSong.Enabled = true;

                btnStop.ForeColor = Color.White;

                btnStop.BackColor = Color.DarkRed;

                lblStatus.Text = strWaveFile + " Now Playing...";

 

            }

 

 

 

8.     Save Your Work.

9.     Build the project to ensure you have no errors.  If so, fix the errors.

10. Test the project.  Load a wave, play and test the pause and stop functionality.

 

While this small application may seem cool to you now, it only plays wave files and no one really uses wave files anymore, except for very small sound effects in gaming.

 

In the next chapter, we are going to use high-level MCI to play MP3 files.

 

On Your Own

 

In this chapter, I tried to keep the code simple but in reality, low-level MCI audio technology is very complicated and there are areas of it that even I do not understand due to poor documentation and lack of audio engineering education.  The Founder of AIPL, Ken Levy, whom I work with on Singulator has a 4-year engineering degree in audio engineering and many years of experience working with audio from the bit level.  In contrast, compared to him, I seem like a beginner.  When I asked him to teach me to build an audio limiting module, he stated that he couldn’t possibly teach me enough engineering in several months to get the job done.  That can only be obtained by going to college and studying the engineering.

 

Likewise, I’ve only touched the surface on low-level MCI in this chapter.  If you are interested in learning more, I have listed a few references at the end of this chapter.

 

Some things you can research to add to our demonstration program include:

 

1.                 Mechanism to stop the audio when the play head is at the end of the buffer.  Currently, the song repeats.  This can be accomplished by obtaining the length in seconds and using a timer, obtain the length of the song in bytes and stopping the audio after it has processed that number of bytes or using the MCI event-triggered MM_WOM_DONE approach,

2.                 Displaying the song’s progress visually in either a progress bar, a time counter or both.

3.                 Jumping to a specific user-defined position in the song.

4.                 Obtaining and Adjusting the sound volume.

5.                 Displaying the amplitude of the audio in the form of a visual VU meter(s).

6.                 Adjust the audio balance (called panning).

7.                 Add a visual effect to the buttons where the currently activated button “blinks”.  This can be accomplished using a Timer control and changing the colors in the Tick event handler.

 

 

Next, we will learn more MCI audio by using the high-level mciSendString approach.

 

Reference:  Programming Windows fifth edition by Charles Petzold(1998)