tag:blogger.com,1999:blog-208612612024-03-12T17:45:53.711-07:00OpenSebJ by DSebJA development blog about my work with C#, NAudio & XNAOpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.comBlogger45125tag:blogger.com,1999:blog-20861261.post-52082932141629299782011-02-12T00:24:00.000-08:002011-02-12T00:24:16.649-08:00New BlogI'm going to migrate my content to my new blog, eventually.<br />
<br />
Check it out for the latest content, including a Tutorial introducing the SunBurn Editor.<br />
<br />
http://dsebj.evolvingsoftware.comOpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-77985761909423876792010-12-27T18:27:00.000-08:002010-12-27T18:27:53.406-08:00Fierce Game Hunting - PublishedAfter a few disagreements between myself and the Back Button, we have resolved any differences and have since had Fierce Game Hunting successfully approved. It’s not currently showing up in the Marketplace search yet (Apparently it can take up to 48 hours after it has been published to become visible) but nether the less, for those of you looking to try it out you can reach it through the following deep link:<br />
<br />
<a href="http://social.zune.net/redirect?type=phoneApp&id=8ea61663-f3ff-df11-9264-00237de2db9e">http://social.zune.net/redirect?type=phoneApp&id=8ea61663-f3ff-df11-9264-00237de2db9e</a><br />
<br />
When I tried this through the desktop version of Zune, with the phone connected, I wasn’t able to download the application to my phone but if you email the link to yourself and open it up on the phone, the Marketplace page on the phone works and you can download it and play the trial straight away.<br />
<br />
I would like to take a moment to thank those of you who provided help, support, advise, guidance, encouragement, influence etc. along the way.<br />
A big shout out to the local Microsoft team, MVP’s & honourable mentions including:<br />
<br />
<a href="http://twitter.com/dglover">Dave Glover</a> – Australia’s heart of Windows Phone 7 & Development<br />
<br />
<a href="http://twitter.com/btroam">Nick Randolph</a> – Great local MVP with excellent knowledge of Windows Phone<br />
<br />
<a href="http://twitter.com/Mykre">Glenn Wilson</a> – Mr AU XNA himself, brilliant level of enthusiasm and encouragement<br />
<br />
<a href="http://twitter.com/ChrisWalshie">Chris Walsh</a> – Not sure why he’s not an MVP yet :-) <br />
<br />
<a href="http://twitter.com/chlong">Christian Longstaff</a> – Great local community support and developmentOpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-57735089172473864612010-12-22T16:41:00.000-08:002010-12-22T16:41:20.828-08:00Live Writer, Blogging and CodePlexI've started maintaining the documentation wiki for NAudio, rather than posting tutorials on my blog (I may do some more so if there are requests please email me) but I think centralizing the documentation that is available for NAudio is a really worthwhile cause. <br />
<br />
Through this process I found out that you can use Live Writer to maintain the documentation on CodePlex (cool) and then found this plugin to allow posting from Visual Studio - http://plugins.live.com/writer/detail/paste-from-visual-studio (even cooler).<br />
<br />
So, if you are maintaining a blog with code, or documentation on CodePlex (or both) then you should check out the combination.<br />
<br />
~DSebJOpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-13101749523425651222010-08-19T17:42:00.000-07:002010-08-19T18:35:18.699-07:00Recording XNA & WP7 Gameplay Videos For FreeI've recently recorded a few game play video's for Fierce Game Hunting and have found a couple of cool programs that make the process remarkably easy and painless, which is really important if you want to be focusing on the game development rather than video editing. Some see the publishing of progress important for the marketing, others will only publish a trailer once the development is complete - up to you really about how much advanced publicity you're looking to achieve however these techniques are universally applicable.<br />
<br />
<b>Taksi</b><br />
Lets have a look at the initial video capture; for a game that is using DirectX hardware rendering, you will want a program which is going to be able to capture all of the frames in their original glory; it's worth noting that not all screen capture programs handle these sorts of scenarios. One program that handles this really well is <a href="http://taksi.sourceforge.net/">TAKSI: Video capture/Screen capture for 3D graphics - Free & Open Source</a>. Taksi takes the hard work out of working out what and how to record and as you probably have a version that runs on your PC (Unless your using Avatars or some such) then you will be good to go. It's a really easy program to use but doesn't appear to be that widely known about. The quality is really good and if your happy with the screen capture, the file can be uploaded directly to YouTube - I've done this with the <a href="http://www.youtube.com/watch?v=OzgDmeBlDhY">Fierce Game Hunting - Game Play Preview 2 Video</a> - I wanted to get it up quickly and this is certainly the shortest path to uploading a result.<br />
<br />
<b>Microsoft Expression Encoder 4</b><br />
If you want to edit your game play preview (crop & remove some blemishes) & perhaps you want to encode it to WMV or Silverlight Experiences for a better file size before uploading - grab a copy of <a href="http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=75402be0-c603-4998-a79c-becdd197aa79">Microsoft Expression Encoder 4 for Free</a> - that's right, this great product - it's actually free. There is a Pro Version as well and you can see from the <a href="http://www.microsoft.com/expression/products/EncoderPro_CompareFeatures.aspx">Feature Comparison Page</a> there are a few restrictions on the Free version (not the trail for the Pro, the free version :-) such as the video's being limited to 10 minutes and that the full range of input formats isn't available but if your using it with Taksi, you have a perfect match for input formats, quality and editing. Pro's certainly worth considering if your looking to edit much longer video's or you need additional export/import formats, but for those Indie game developers out there, it's probably not a necessarily requirement.<br />
<br />
Hope this helps some more XNA Indies get the word out about what they have been working on, these programs have helped me heaps and they haven't got the level of recognition they deserve.<br />
<br />
~DSebJOpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com1tag:blogger.com,1999:blog-20861261.post-84760612121127616422010-08-14T02:28:00.000-07:002010-08-14T02:28:39.272-07:00WP7 XNA Fierce Game Hunting - Game Play Preview 2Fierce Game Hunting is an XNA Game for Windows Phone 7. This Game Play Preview shows the opening scene of the game, with Stags being hunted before any firing begins.<br />
<br />
<object width="548" height="300"><param name="movie" value="http://www.youtube.com/v/OzgDmeBlDhY?fs=1&hl=en_GB&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/OzgDmeBlDhY?fs=1&hl=en_GB&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="548" height="300"></embed></object><br />
<br />
Preview 2 brings together the elements highlighted individually - Panoramic scrollable backgrounds, the Stag animations and the blending of the environment to demonstrate the opening scene. No weapons previews included in this clip.<br />
<br />
@DSebJ OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com2tag:blogger.com,1999:blog-20861261.post-9874446212091372382010-08-08T04:56:00.000-07:002010-08-08T04:56:51.386-07:00Preview of Windows Phone 7 Development<div class="mobile-photo"></div><div class="mobile-photo"></div><div class="mobile-photo"></div>After releasing Operation Old Spice and learning a lot of valuable lessons about producing and releasing an Indie game, my foray in to Windows Phone 7 development has commenced. The title I've been working on is going to be called Fierce Game Hunting. I spent one of my days this weekend animating a Stag; a tiring experience as I did it all by hand with the help of Paint.Net - I did have a look at some animation software before I started but couldn't really find something that quite meet my needs and had a low learning curve (if your an actual artist and can draw what ever it is that your going to animate, Pencil looks like a promising tool, even through it hasn't had a new release in years.) <br />
<br />
Note to self: Need to review the possibility to use Expression Blend to build the Animation, after segmenting the image in to a number of layers and then work outta way to save out each of the frames after they have been positioned in to individual frame images. <br />
<br />
I posted a really alpha preview of the animation on You Tube and it got picked up by <a href="http://wmpoweruser.com/fierce-game-hunting-hunting-game-for-windows-phone-7/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+WmPowerUser+%28WM+Power+User%29&utm_content=Twitter">wmpoweruser.com</a> really quickly. A few things I didn't account for were the distortion of the video after being loaded to You Tube and that the Deer Animation was part of my test for reusing different frames to change the actual animation on the fly - which resulted in a deer looking like it was on steroids while eating - which isn't a very good representation of what will be going on :-)<br />
<br />
<a href="http://3.bp.blogspot.com/_Ziytxk6N3oM/TF6akiJQ71I/AAAAAAAAADo/8uSwnS84f28/s1600/Deer-770252.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5503005747124957010" src="http://3.bp.blogspot.com/_Ziytxk6N3oM/TF6akiJQ71I/AAAAAAAAADo/8uSwnS84f28/s320/Deer-770252.png" /></a>To give a better feel of what I expect this to look like in terms of quality and approach see the following static images, which are taken in the correct aspect resolution for the game - One of the Deer in the woods, another with the scope on and the final image demonstrates the Panorama images being used for the background, which can be scrolled to find and hunt the prey.<br />
<br />
<a href="http://4.bp.blogspot.com/_Ziytxk6N3oM/TF6aliYhAVI/AAAAAAAAADw/JuevbPdcMRw/s1600/DeerSights-774808.png" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5503005764368793938" src="http://4.bp.blogspot.com/_Ziytxk6N3oM/TF6aliYhAVI/AAAAAAAAADw/JuevbPdcMRw/s320/DeerSights-774808.png" /></a><br />
It's still quite early in the development cycle but keen for any (constructive) feedback. I have received some comments about the way it looks and it certainly has got me thinking about how unforgiving using actual photos actually is - if there is something out of place in a photo it's really noticeable, if it's out of place in a drawing then your eye is more forgiving - I was planning on going for a more humours animation style using the photographic imagery but it seems that this approach has been interpreted as being more serious - which is a great insight in and of itself, meaning that some more unrealistic and fun game play elements should be mixed in, to let the game take it's self a little less seriously.<br />
<a href="http://2.bp.blogspot.com/_Ziytxk6N3oM/TF6aj77CVTI/AAAAAAAAADg/KUejKBEWG4E/s1600/DeerPanorama-766358.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="" border="0" height="164" id="BLOGGER_PHOTO_ID_5503005736864732466" src="http://2.bp.blogspot.com/_Ziytxk6N3oM/TF6aj77CVTI/AAAAAAAAADg/KUejKBEWG4E/s640/DeerPanorama-766358.png" width="640" /></a> <br />
<br />
~DSebJOpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-14006688333508952352010-01-21T06:41:00.000-08:002010-01-21T06:55:19.614-08:00The feeling of a shifting breezeFor a long time I have been an avid Open Source developer; I'm not proclaiming that this wont continue but I do feel a breeze of change blowing across my thoughts. <br /><br />That is, what is the general users expectation from a developer, or group of developers for the software that they have developed? In addition to this, how does the average user factor the amount of money they have paid for the product in to how they feel about the product, does it temper expectations?<br /><br />I read this post from Nick tonight and feel this has reflected some of the questions I have recently been considering, albeit a slightly different amount, Little rather than Free:<br /><a href="http://nickgravelyn.com/2009/5/why-i-hate-the-app-store-gamer/">http://nickgravelyn.com/2009/5/why-i-hate-the-app-store-gamer/</a><br /><br />Perhaps this is the same thing or at the very least, similar, that I have found slightly off putting about Open Source development today.OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-70262213014962594562009-09-17T14:33:00.001-07:002009-09-17T14:33:30.444-07:00NAudio Tutorial 7 – The Basics of MIDI Files<h2>Preamble </h2> <p>After the invigorating ride with the MIDI interface, I've done what I didn't originally set out to do and fallen for MIDI. It's been a bit of an arms length association for me; I actually started developing OpenSebJ (and BeatIt before that) many years ago because I didn't want to buy a MIDI keyboard and because I admittedly wasn't impressed with what what I associated with MIDI – that tinny sound that streams through your speakers when you started browsing the internet, after founding some ones home page on a free hosting site, that thought it would be wonderful to share with you a piece of music that could only be pitifully rendered through some inbuilt wave table on your Sound Blaster 16 (if you were so fortunate).</p> <p>I digress; however that history is somewhat important as my focus has shifted since those humble beginnings to now understanding that MIDI does have a role in my future, for two primary reasons</p> <p>1) It's the industry standard for interfacing Audio Equipment with a computer</p> <p>2) It's a standard file format that can be read and written by most audio applications and means that layout's and scores using this information are almost universally transferable.</p> <p>Don't get me wrong, I'm still a sucker for samples and that's where I'll end up targeting all of my development and time any way but MIDI in and of itself, is certainly an assisting means to that end. </p> <p>This NAudio tutorial will be focusing on the MIDI File Format; we will start with the basics before moving on to the more intricate elements within the format. If you haven't had a chance to review the other posts in the NAudio Tutorial series yet, you can find them here:</p> <p><a href="http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html">http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html</a> <br /><a title="http://opensebj.blogspot.com/2009/08/naudio-tutorial-6-midi-interfacing_27.html" href="http://opensebj.blogspot.com/2009/08/naudio-tutorial-6-midi-interfacing_27.html">http://opensebj.blogspot.com/2009/08/naudio-tutorial-6-midi-interfacing_27.html</a></p> <h2>The Format of Events</h2> <p>We can basically think of a MIDI file as a collection of events. These events are the same type of events which were introduced in the previous tutorial. The NoteOnEvent is arguably the most important and it is made up of:</p> <p>AbsoluteTime – The time when this event will occur, in milliseconds <br />Channel – The channel (or you can think of it as the instrument), which this event relates to <br />NoteNumber – The number for the note; basically each note is assigned a number and this is how we work out which note on the scale will be played. Have a look at this nice <a href="http://upload.wikimedia.org/wikipedia/commons/7/7a/NoteNamesFrequenciesAndMidiNumbers.svg" target="_blank">SVG on Wikipedia</a> which explains it. <br />Velocity – How hard we want to play the note <br />NoteLength (Duration) – How long the note is to be played for</p> <p>So to put this together and create an event:</p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">int</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> AbsoluteTime = 1000</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">; <span style="color: green">// 1 Second in on the track <br /></span></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">int</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> Channel = 1; <span style="color: green">// Channel needs to be between 1 and 16 </span> <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">int</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> NoteNumber = 54; <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">int</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> Velocity = 127; </span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes">// Velocity is from 0 which is considered off, to 127 which is the maximum <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">int</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> Duration = 250; <br /> <br /></span><span style="font-size: 10pt; color: #2b91af; font-family: "Courier New"; mso-no-proof: yes">NoteOnEvent</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> note1On = <span style="color: blue">new</span> <span style="color: #2b91af">NoteOnEvent</span>(AbsoluteTime, Channel, NoteNumber, Velocity, Duration);</span></p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; color: #2b91af; font-family: 'Courier New'; mso-no-proof: yes">NoteOnEvent</span><span style="font-size: 10pt; font-family: 'Courier New'; mso-no-proof: yes"> note1Off = <span style="color: blue">new</span> <span style="color: #2b91af">NoteOnEvent</span>(AbsoluteTime + Duration, Channel, NoteNumber, 0, 0); <span style="color: green">// This is in effect a note off event – letting us know that the note can stop playing now.</span></span></p> <p class="MsoNormal" style="mso-layout-grid-align: none">Each NoteOn needs a corresponding NoteOff. A note off is defined by the Velocity == 0. If we don't have a corresponding NoteOff for a NoteOn event we will get a lovely exception thrown informing us of our civic duty to add a NoteOff for every NoteOn. </p> <p class="MsoNormal" style="mso-layout-grid-align: none">One note on and note off event by itself is interesting but not very useful. If we want to keep a set of events together then we should use the MidiEventCollection.</p> <h2>The Collection of Events</h2> <p class="MsoNormal" style="mso-layout-grid-align: none">A MidiEventCollection is exactly what the name suggests, a collection of MIDI events. However it is a very sophisticated collection and is structured in such a way that allows for easy translation to a Midi file when required. If we have a look at the constructor we have the following:</p> <p class="MsoNormal"><span style="font-size: 10pt; color: #2b91af; font-family: "Courier New"; mso-no-proof: yes">MidiEventCollection</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> events = <span style="color: blue">new</span> <span style="color: #2b91af">MidiEventCollection</span>(FileType, DeltaTicksPerQuarterNote);</span><span style="font-size: 10pt; font-family: "Arial","sans-serif"; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi"> </span></p> <p></p> <p></p> <p></p> <p>The file type is referring to what format we will be using for the MIDI File – we can set this to one for the purposes of this demonstration. </p> <p>DeltaTicksPerQuarterNote is what it implies but we wont be going in to detail on this item in this tutorial, for now you can just set it to a value of 120.</p> <h2>Tracks</h2> <p>The MIDI specification can contain a number of tracks (think separate instruments) within the one file. Therefore each Event needs to be associated to a Track. In the version of the MIDI file we are working with in this example, Track 0 is used to store basic meta data about the composition. We add tracks to the MidiEventCollection like so:</p> <p class="MsoNormal"><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">int</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> outputTrackCount = 2; <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">for</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> (<span style="color: blue">int</span> track = 0; track < outputTrackCount; track++) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{ <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-tab-count: 1">      </span>events.AddTrack(); <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">} </span></p> <p></p> <p></p> <p></p> <h2>Add Events to the Collection</h2> <p>To add an event to a track all we need to use the Add method of the MidiEventCollection class. The Track is used as the array position identifier and the method then stores the events on that track – like so:</p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">events[1].Add(noteOn1); <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">events[1].Add(noteOff1);</span></p> <h2>Export the Collection to a file (Save MIDI File)</h2> <p>Quick recap, we now have a single note being played defined, which is made up of 2 events, a NoteOn event and a corresponding NoteOff event. We have added 2 tracks to the MidiEventCollection, Track 0 & Track 1 and finally we have added the 2 events to Track 1. Before we export our lone playing note composition EndMarkers need to be appended to <em><strong>each</strong></em> Track. Fortunately there is a pre-supplied function for this which makes it rather straight forward, you will need to add it to your class though:</p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">private</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <span style="color: blue">void</span> AppendEndMarker(<span style="color: #2b91af">IList</span><<span style="color: #2b91af">MidiEvent</span>> eventList) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{ <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span><span style="color: blue">long</span> absoluteTime = 0; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span><span style="color: blue">if</span> (eventList.Count > 0) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">        </span>absoluteTime = eventList[eventList.Count - 1].AbsoluteTime; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span>eventList.Add(<span style="color: blue">new</span> <span style="color: #2b91af">MetaEvent</span>(<span style="color: #2b91af">MetaEventType</span>.EndTrack, 0, absoluteTime)); <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">}</span><span style="font-size: 10pt; font-family: "Arial","sans-serif"; mso-bidi-font-size: 11.0pt; mso-bidi-font-family: 'Times New Roman'; mso-bidi-theme-font: minor-bidi"> </span></p> <p></p> <p></p> <p></p> <p>Then it's just a matter of calling the method:</p> <p><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">AppendEndMarker(events[0]); <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">AppendEndMarker(events[1]);</span></p> <p>After this it's matter of calling the Export function and passing in the file name where the file is to be saved and the MidiEventCollection storing all of the events, aka:</p> <p class="MsoNormal"><span style="font-size: 10pt; color: #2b91af; font-family: "Courier New"; mso-no-proof: yes">MidiFile</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">.Export(filename, events);<span style="color: blue"> </span></span></p> <p></p> <p></p> <p></p> <p></p> <p>That's seriously it. We have saved our Midi file to some location. Go play it and hear a single note, exciting.</p> <h2>Other NAudio Tutorials</h2> <p>For more tutorials in this series, please see the following:</p> <a href="http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html">http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html</a> <br /><a title="http://opensebj.blogspot.com/2009/08/naudio-tutorial-6-midi-interfacing_27.html" href="http://opensebj.blogspot.com/2009/08/naudio-tutorial-6-midi-interfacing_27.html">http://opensebj.blogspot.com/2009/08/naudio-tutorial-6-midi-interfacing_27.html</a> OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com6tag:blogger.com,1999:blog-20861261.post-6450404637689026922009-08-30T15:59:00.001-07:002009-09-14T15:17:28.142-07:00UI Threading on WPF<span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'">While trying to hook up a number of development components, I needed to call a method in a custom control that would update the fill of a rectangle. Calling this method, from within the event of a ButtonClick worked fine, however when I attempted to programmatically access the same method within the Custom Control I encountered an error: <br /> <br />InvalidOperationException - “The calling thread cannot access this object because a different thread owns it.” <br /> <br />It’s all due to the multi threaded nature of what’s going on in the background and thankfully the multithreading is going on, giving us our dynamic, performing environment. But there is a slight addition in complexity to resolve this issue, which is the </span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Courier New'; mso-no-proof: yes">Dispatcher <br /> <br /></span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'">The </span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Courier New'; mso-no-proof: yes">Dispatcher </span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'">is linked to the UI thread to handle events which require access to UI elements, and in the WPF world it looks a bit simpler than the world of Win Forms. To utilise the </span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Courier New'; mso-no-proof: yes">Dispatcher </span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'">there is a bit of setup to complete. <br /> <br />To put this example in context, look at the following screen shot: <br /> <br /><Screen shot to come another day, just imagine a piano keyboard for now> <br /></span>  <p><span style="font-size: 10.5pt; font-family: "Times New Roman"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa"><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'">This is part of a larger custom control, representing a keyboard scale. Each key is made up of a rectangle. The fill can be replaced with an alternate fill to indicate that the key is down and subsequently reverted back to the original fill when the key is released. This control has 2 methods to invoke this functionality:</span><span style="font-size: 10.5pt; font-family: "Times New Roman"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa"> <br /> <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">ColourKey(<span style="color: blue">int</span> key); <br />UnColourKey(<span style="color: blue">int</span> key);</span><span style="font-size: 10.5pt; font-family: "Times New Roman"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa"> <br /> <br /></span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'">An instance of this control has been created and added on to a WPF window and linked via the MIDI Framework provided by NAudio, to a MIDI Controller Keyboard. These methods on the custom control can’t be called directly from a method not being invoked from the UI – i.e not a button click. As such we need to utilise a delegate, I’ve opted for two in this example. <br /> <br /></span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">//// This delegate enables asynchronous calls for setting <br />//// the Colour of the key <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">delegate</span><span style="font-size: 10pt; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> <span style="color: blue">void</span> <span style="color: #2b91af">dColourKey</span>(<span style="color: blue">int</span> key); <br /><span style="color: blue">delegate</span> <span style="color: blue">void</span> <span style="color: #2b91af">dUnColourKey</span>(<span style="color: blue">int</span> key); <br /> <br /></span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"> <br />These delegates provide a safe way to call the methods which have been setup on the custom control.: <br /> <br /></span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> </span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"><summary> <br />///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> Called via the delegate after using this.Dispatcher.Invoke <br /></span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> </span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"></summary> <br />///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> </span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"><param name="key"></span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">The key to colour on the scale control</span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"></param> <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">private</span><span style="font-size: 10pt; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> <span style="color: blue">void</span> ColourKey(<span style="color: blue">int</span> key) <br />{ <br /><span style="mso-spacerun: yes">    </span>scale1.ColourKey(key); <br />} <br /> <br /><span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray"><summary> <br />///</span><span style="color: green"> Called via the delegate after using this.Dispatcher.Invoke <br /></span><span style="color: gray">///</span><span style="color: green"> </span><span style="color: gray"></summary> <br />///</span><span style="color: green"> </span><span style="color: gray"><param name="key"></span><span style="color: green">The key to UnColour on the scale control</span><span style="color: gray"></param> <br /></span><span style="color: blue">private</span> <span style="color: blue">void</span> UnColourKey(<span style="color: blue">int</span> key) <br />{ <br /><span style="mso-spacerun: yes">    </span>scale1.UnColourKey(key); <br />}</span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"> <br /> <br /> <br />To access these methods, via the delegate we need to use the following; <br /> <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">this</span><span style="font-size: 10pt; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">.Dispatcher.Invoke(<span style="color: blue">new</span> <span style="color: #2b91af">dColourKey</span>(ColourKey),ne.NoteNumber);</span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"> <br /> <br /> <br />A small explanation is in order. The first part is obviously calling the Invoke method on the Dispatcher. The two parameters are where it all happens. The first is initialising the delegate, and passing in the name of the method which is to be called – in this example, the name of the method is ColourKey: <br /> <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes">new</span><span style="font-size: 10pt; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> <span style="color: #2b91af">dColourKey</span>(ColourKey) <br /> <br /> <br /></span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'">The second is the argument which will be passed to the calling method & in this case it’s the number of the note which is being played: <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-no-proof: yes"> <br />ne.NoteNumber <br /> <br /></span><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"> <br />This small overhead ensures that we don’t have a situation where our InvalidOperationException will be generated. <br /> <br /> <br /></span> <h3>Extended Learning:</h3> <br />One of the reasons I like to write up items like this is so that I can increase my own understanding about the topic. Being able to explain a concept to some one else not only assists the person who your explaining it to but reinforces your own understanding and through writing this post I’ve opened my own eyes further. I’ve refracted my own code and included the Dispatcher and Delegate code in the custom control; which means it’s no longer required by the calling WPF form. Amazing.</span><span style="font-size: 12pt; font-family: "Times New Roman"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa"> <br /> <br style="mso-special-character: line-break" /> <br style="mso-special-character: line-break" /></span></span></span></p> <p><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"><span style="font-size: 10.5pt; font-family: "Trebuchet MS"; mso-fareast-font-family: 'Times New Roman'; mso-ansi-language: en-au; mso-fareast-language: en-au; mso-bidi-language: ar-sa; mso-bidi-font-family: 'Times New Roman'"></span></span></p> OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-52798080865878156982009-08-27T17:39:00.001-07:002009-08-27T17:41:26.155-07:00NAudio Tutorial 6 - MIDI Interfacing<p></p> <p class="MsoNormal">It’s been a while. </p> <p></p> <p class="MsoNormal">So kicking off from where we left off this instalment is going to be looking at MIDI and how we can interoperate control characters sent through the <place w:st="on">MIDI</place> interface in our application. This tutorial as per the other tutorials in the series assumes that you have read the previous tutorials, as we will be building upon concepts and understanding from each of these sections. Feel free to read through this tutorial cold but if your looking for the background for anything not covered here it would be best to check out the other Tutorials as a first stop.</p> <p class="MsoNormal" style="mso-margin-top-alt: auto; mso-margin-bottom-alt: auto"><a href="http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html">http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html</a></p> <p class="MsoNormal">As you may by now expect, NAudio has a set of functions for this as well. You will find the useful set of functions under <span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">NAudio.Midi; </span> </p> <p></p> <p></p> <h2>Setting It </h2> <p></p> <p></p> <p></p> <p class="MsoNormal">Lets create a class to encapsulate the bulk of the <place w:st="on">MIDI</place> functionality that we will be calling upon for this tutorial.</p> <p></p> <blockquote> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">using</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> NAudio.Midi;</span> <br /><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">namespace</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> AudioInterface <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{ <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span><span style="color: blue">public</span> <span style="color: blue">class</span> <span style="color: #2b91af">NAudioMIDI <br /></span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">       </span><span style="color: blue">public</span> <span style="color: #2b91af">MidiIn</span> midiIn;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">       </span><span style="color: blue">private</span> <span style="color: blue">bool</span> monitoring;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">       </span><span style="color: blue">private</span> <span style="color: blue">int</span> midiInDevice; </span></p> </blockquote> <p></p> <p></p> <p></p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"></span></p> <p>Our midiInDevice represents what MIDI device on the system we want to use for this interface; in case you have more than one <place w:st="on">MIDI</place> device connected to your system. I only have a single MIDI device however going through this process is obviously useful for those who have more than one and it’s useful to check that the <place w:st="on">MIDI</place> device I have is actually plugged in and switched on. <br /> <br />Once we have defined what <place w:st="on">MIDI</place> device we will be using, it will be initiated and the midiIn instance will relate to that device.<span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <br /> <br /></span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes">///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes"> </span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes"><summary> <br /></span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes">///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes"> Get a list of MIDI Devices <br /></span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes">///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes"> </span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes"></summary> <br /></span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes">///</span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes"> </span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes"><returns></span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes">string[] of MIDI Device Names</span><span style="font-size: 10pt; color: gray; font-family: "Courier New"; mso-no-proof: yes"></returns></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">public</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <span style="color: blue">string</span>[] GetMIDIInDevices() <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: green">// Get a list of devices</span>  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: blue">string</span>[] returnDevices = <span style="color: blue">new</span> <span style="color: blue">string</span>[<span style="color: #2b91af">MidiIn</span>.NumberOfDevices];  <br /> <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: green">// Get the product name for each device found</span>  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span></span><span lang="FR" style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes">for</span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"> (<span style="color: blue">int</span> device = 0; device < <span style="color: #2b91af">MidiIn</span>.NumberOfDevices; device++)  <br /></span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>returnDevices[device] = <span style="color: #2b91af">MidiIn</span>.DeviceInfo(device).ProductName;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span>}  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: blue">return</span> returnDevices; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">} <br /> <br /></span>Assuming that we want to allow the user to select a Device from a list of Device’s then we would pass this list back to a control which will populate this list with the available devices. With something like this from our Load method on the form class: <br /> <br /><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">private</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <span style="color: blue">void</span> NAudioTutorial6_Load(<span style="color: blue">object</span> sender, <span style="color: #2b91af">EventArgs</span> e) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{ <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><SNIP> <br /> <br /></span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>// Populate the devices available for the <place w:st="on">MIDI</place> interface  <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>string</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">[] MIDIDevices = AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.nMIDI.GetMIDIInDevices(); <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>foreach</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> (<span style="color: blue">string</span> devices <span style="color: blue">in</span> MIDIDevices) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>boxMIDIIn.Items.Add(devices);  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>}  <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>try  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>boxMIDIIn.SelectedIndex = 0;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>}<span style="color: blue">catch</span>(<span style="color: #2b91af">Exception</span> except){  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>System.Windows.Forms.<span style="color: #2b91af">MessageBox</span>.Show(<span style="color: #a31515">"No <place w:st="on">MIDI</place> Device Detected"</span>);  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>} <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><SNIP> <br /> <br /></span><b style="mso-bidi-font-weight: normal"></b></p> <h2><b style="mso-bidi-font-weight: normal">Starting It </b></h2> <p></p> <p></p> <p></p> <p class="MsoNormal" style="mso-layout-grid-align: none">Brilliant, so now we have a list of available <place w:st="on">MIDI</place> devices, loaded in to a list box control, that the user can select from. Now we need to know when the user has actually chosen the <place w:st="on">MIDI</place> control they would like us to monitor; so let’s put in a button on our UI to trigger this. <br /> <br /><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">private</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <span style="color: blue">void</span> cmbMonitor_Click(<span style="color: blue">object</span> sender, <span style="color: #2b91af">EventArgs</span> e) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span><span style="color: green">// Setup the <place w:st="on">MIDI</place> interface to start monitoring the selected device</span>  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes"> </span><span style="mso-spacerun: yes">   </span>AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.nMIDI.StartMonitoring(boxMIDIIn.SelectedIndex);  <br /> <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes"> </span><span style="mso-spacerun: yes"> </span><span style="mso-spacerun: yes">  </span><span style="color: green">// Add the event handler, to handle the <place w:st="on">MIDI</place> messages received <br /></span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span>AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.nMIDI.midiIn.MessageReceived += <span style="color: blue">new</span> <span style="color: #2b91af">EventHandler</span><<span style="color: #2b91af">MidiInMessageEventArgs</span>>(midiIn_MessageReceived); <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">} <br /> <br /></span>When StartMonitoring is called we through back to the nMIDI instance we created earlier and (using the selected MIDI device) setup the midiIn device and set the midiIn device to Start – which in turn, kicks NAudio in to gear to start monitoring MIDI messages received from the MIDI device.<span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <br /> <br /></span><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">public</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <span style="color: blue">void</span> StartMonitoring(<span style="color: blue">int</span> MIDIInDevice) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes"> </span><span style="mso-spacerun: yes">     </span><span style="color: blue">if</span> (midiIn == <span style="color: blue">null</span>)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>midiIn = <span style="color: blue">new</span> <span style="color: #2b91af">MidiIn</span>(MIDIInDevice);  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>}  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>midiIn.Start();  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">      </span>monitoring = <span style="color: blue">true</span>; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">} <br /></span> <br />Going back to cmbMonitor(…) we next setup the EventHandler for the <place w:st="on">MIDI</place> messages which are going to be received: <br /> <br /><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes">// Add the event handler, to handle the <place w:st="on">MIDI</place> messages received</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.nMIDI.midiIn.MessageReceived += <span style="color: blue">new</span> <span style="color: #2b91af">EventHandler</span><<span style="color: #2b91af">MidiInMessageEventArgs</span>>(midiIn_MessageReceived); <br /> <br /></span><b style="mso-bidi-font-weight: normal"></b></p> <h2>Playing It</h2> <p class="MsoNormal" style="mso-layout-grid-align: none">For this to work, we need to have an event handler method setup to receive the messages, within the same class. From the line above you should see that the method is midiIn_MessageReceived - which we will have a look at now: <br /> <br /><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">public</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">  </span><span style="color: blue">void</span> midiIn_MessageReceived(<span style="color: blue">object</span> sender, <span style="color: #2b91af">MidiInMessageEventArgs</span> e) <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: green">// Exit if the MidiEvent is null or is the AutoSensing command code</span>  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: blue">if</span> (e.MidiEvent != <span style="color: blue">null</span> && e.MidiEvent.CommandCode == <span style="color: #2b91af">MidiCommandCode</span>.AutoSensing)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="mso-spacerun: yes">     </span><span style="color: blue">return</span>;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span>} <br /> <br /></span>Assuming that MIDI Event Command Code represents a Note On Event, then we need to interpret what Note On Event has been sent. To do this we need to cast the MidiEvent to a NoteOnEvent:<span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">             <br /> <br /></span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: blue">if</span> (e.MidiEvent.CommandCode == <span style="color: #2b91af">MidiCommandCode</span>.NoteOn)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="color: green">// As the Command Code is a NoteOn then we need <br />          // to cast the MidiEvent to the NoteOnEvent</span>  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span></span><span lang="FR" style="font-size: 10pt; color: #2b91af; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes">NoteOnEvent</span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"> ne;  <br /></span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>ne = (<span style="color: #2b91af">NoteOnEvent</span>)e.MidiEvent; <br /> <br /></span>ne is now a NoteOnEvent which has some specific MIDI attributes, such as a NoteNumber, which is an int that represents a single note from the full scale; as well as a Velocity which represents how hard the MIDI note has been played, in this example it how hard was the MIDI controller pressed (assuming that the MIDI controller you have can report this information ala levels of sensitivity). <br /> <br />Each NoteNumber represents an incremental note on the scale, starting with C0 == 0, Db0 == 1 (C# aka D-before-0), D0 ==2, Eb0 ==3 (D#), E0 == 4 etc. this relationship continues on. For practical purposes (read number of samples for the Piano scale that I have, tops out at 96 which is C8)<span style="mso-spacerun: yes">  </span>two sets of notes have been mapped within the NAudioInterface class, in a single array. The first set of notes, 0 – 100 are consider mf (quite). Notes 100 – 200 represent the same positions, but contain samples loaded that are ff (loud). Separating by a round 100 makes all the additions and subtractions to interface with these notes rather straight forward. This mapping is contained within the vKeys Class and is a whole heap of excitement, if a long list of static mappings is your thing. A snip-it of the class: <br /> <br /><span style="font-size: 10pt; color: blue; font-family: "Courier New"; mso-no-proof: yes">public</span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> <span style="color: blue">static</span> <span style="color: blue">class</span> <span style="color: #2b91af">vKeys <br /> <br /></span></span><SNIP> <br /> <br /><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">vFFKeysFileNames[48] = <span style="color: #a31515">"ff.C4.wav"</span>; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">vFFKeysFileNames[49] = <span style="color: #a31515">"ff.Db4.wav"</span>; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">vFFKeysFileNames[50] = <span style="color: #a31515">"ff.D4.wav"</span>; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">vFFKeysFileNames[51] = <span style="color: #a31515">"ff.Eb4.wav"</span>; <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">vFFKeysFileNames[52] = <span style="color: #a31515">"ff.E4.wav"</span>; <br /> <br /></span><SNIP> <br /> <br />Ohh Ahh.. <br /> <br />Back to the velocity, so we have a number, ne.Velocity which represents how hard the note has been played, as such we use that to then work out what sample should be played. If it’s less then 50, then the quite sample is played, else loud. <br /> <br /><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="color: blue">if</span> (ne.Velocity < 50)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="mso-spacerun: yes">     </span>AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.Play(ne.NoteNumber);  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>}  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="color: blue">else  <br /></span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="mso-spacerun: yes">     </span>AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.Play(ne.NoteNumber + 100);  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>}<span style="mso-spacerun: yes">   <br /></span></span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"><span style="mso-spacerun: yes">    </span><span style="mso-spacerun: yes"> </span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">} </span></p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"></span></p> <h2>Stoping It </h2> <p></p> <p></p> <p></p> <p class="MsoNormal" style="mso-layout-grid-align: none">This means that we can now play a note and conversely we need to be able to stop playing a note. To fulfil this requirement we have the following, which is effectively the converse with the exception that no checking of the Velocity is required, instead all related samples are requested to be faded out, both the loud and the soft. One may ask whys that, basically a model of the real instrument. When a single note in an instrument stops playing, all of the note stops playing. If it had first been played softly and then loudly but then has stoped being played, then the note is no longer being played – regardless of original velocity. To this end, both sets of the notes are Faded Out.<span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">  <br /> <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: blue">if</span> (e.MidiEvent.CommandCode == <span style="color: #2b91af">MidiCommandCode</span>.NoteOff)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="color: #2b91af">NoteEvent</span> ne;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span></span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes">ne = (<span style="color: #2b91af">NoteEvent</span>)e.MidiEvent;  <br /> <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.FadeOut(ne.NoteNumber); <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.FadeOut(ne.NoteNumber + 100);  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span>} </span>  </p> <h2>Changing It </h2> <p></p> <p></p> <p></p> <p class="MsoNormal" style="mso-layout-grid-align: none">The home stretch and in fact this could easily be left off. This last section relates to a controller value being changed. The controller, at least on the <place w:st="on">MIDI</place> device I have represents a set of buttons and knobs – the following code is more fixed then you would put in production code but it suits the purpose of a tutorial and most important, scratches an itch. <br /> <br />Determining if this is a ControlChange event and assuming it is, then the MidiEvent needs to be cast to a ControlChangeEvent:<span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"> </span> </p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="color: blue">if</span> (e.MidiEvent.CommandCode == <span style="color: #2b91af">MidiCommandCode</span>.ControlChange)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="color: #2b91af">ControlChangeEvent</span> cce;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>cce = (</span><span lang="FR" style="font-size: 10pt; color: #2b91af; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes">ControlChangeEvent</span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes">)e.MidiEvent;</span></p> <span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"> <p class="MsoNormal" style="mso-layout-grid-align: none"> <br /></p> Similar to the NoteOnEvent, the ControlChangeEvent has a numerical value, the attribute Controller - which is used to determine which Controller’s value has been changed. For this example we are only monitoring one specific controller, 71. The individual notes above also have the attribute of sensitivity, similarly Controllers have a ControllerValue. The ControllerValue is a value in the range of 0 – 127. This controller has been used to define the time out value for the notes which are played. The longer the fade out, the more of the note duration is heard.</span> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span><span style="color: blue">if</span> ((<span style="color: blue">int</span>)cce.Controller == 71)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">               </span><span style="color: blue">int</span> timeOutValue;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="mso-spacerun: yes">          </span><span style="color: blue">if</span> (cce.ControllerValue < 127)  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">               </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">                  </span><span style="mso-spacerun: yes">  </span><span style="color: green">// Calculate a sliding value for the fade out based on the  <br /></span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">                    </span><span style="color: green">// ControllerValue. This could be drematically improved..  <br /></span></span><span style="font-size: 10pt; color: green; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">                    </span>// It is meant to be very granular at one end and more extreme  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">                    </span><span style="color: green">// at the other but the calculation could surley be improved.</span><span style="mso-spacerun: yes">   <br /></span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">                    </span>timeOutValue = (<span style="color: blue">int</span>)<span style="color: #2b91af">Math</span>.Exp(<span style="color: #2b91af">Math</span>.Log(cce.ControllerValue) * 1.75);  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">               </span>}  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">               </span><span style="color: blue">else  <br /></span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span><span style="mso-spacerun: yes">          </span>{  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">                    </span>timeOutValue = 100000;  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">               </span>}  <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span></span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">AudioInterface.<span style="color: #2b91af">NAudioInterface</span>.SetFadeOut(timeOutValue);  <br /></span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"><span style="mso-spacerun: yes">          </span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">}  <br /></span><span lang="FR" style="font-size: 10pt; font-family: "Courier New"; mso-ansi-language: fr; mso-no-proof: yes"><span style="mso-spacerun: yes">     </span></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">} <br /></span><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes">}</span></p> <p class="MsoNormal" style="mso-layout-grid-align: none"><span style="font-size: 10pt; font-family: "Courier New"; mso-no-proof: yes"></span></p> <h2><b style="mso-bidi-font-weight: normal">Finishing It </b></h2> <p></p> <p></p> <p></p> <p class="MsoNormal">Tha, tha, that’s all folks.</p> <p class="MsoNormal">For more NAudio guidance, please review the other NAudio tutorials in the series. <br /></p> <a href="http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html"><span lang="EN-US" style="mso-ansi-language: en-us">http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html</span></a> <br /><a href="http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html">http://opensebj.blogspot.com/2009/04/naudio-tutorial-5-recording-audio.html</a> OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com4tag:blogger.com,1999:blog-20861261.post-11196413582870464812009-04-09T17:15:00.000-07:002009-04-09T19:33:14.410-07:00MIDI Controller AcquiredFinally I've entered the world of MIDI. I have stood my distance ever since I started looking at Audio Composition, Programming and Development because of a perceived cost barrier and maybe I was right at the time, however things change and if the price hasn't then the accessibility of the price has.<br /><br />Yesterday I handed over AUS $185 for a UMX49 made by Behringer, which is just a MIDI controller with some bundled software; <a href="http://www.behringer.com.au/EN/Products/UMX49.aspx">http://www.behringer.com.au/EN/Products/UMX49.aspx</a><br /><br />Why so compelling a purchase? Well it comes with a cut down copy of Ableton 4 Live (Lite Behringer Edition) and some other, free and Open Source programs - which you can actually download from their website here: <a href="http://www.behringer.com.au/EN/Support/U-Control-Downloads.aspx">http://www.behringer.com.au/EN/Support/U-Control-Downloads.aspx</a> it looks like a nice collection of VST plugins, with every body's favourite (or at least mine) Open Source Audio Sample Production Tool, Audactiy. But some software by itself isn't that compelling, especially if OpenSebJ is missing, so what else? Well the package also includes a USB "Sound Card" - the UCA200 which doesn't appear to be sold separately but seems to be basically like the UCA202 except for a missing Optical Output, Headphone Jack and Volume Slider no idea if the internals are the same but for comparison the UCA202 is priced around AUS $50; and I was thinking of getting one of those previously to test if I received a reduction of timing in latency for ASIO.<br /><br />So what? Well it looks nice too. The real key thing for me is that now I have a way to test MIDI signals and hook them up in the audio tools that I am developing, soon you will be able to forget Ableton, OpenSebJ will be coming to a home near you, with MIDI interface support. vScaleNotes will get a face lift and start allowing all the keys on the fully sampled piano to be playable via MIDI, so people who actually know how to play a keyboard, rather than type, will actually be able to use it and produce some wonder sounds.<br /><br />Bring on the MIDI.<br /><br />A word from our sponsors: We have none but if you would like to become a sponsor of OpenSebJ and vScaleNotes, please let us know what Audio Hardware you could supply to feed our new Audio Hardware craving, hmm or a shirt and hat would be nice too.OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com1tag:blogger.com,1999:blog-20861261.post-80863356762475884432009-04-05T01:03:00.001-07:002009-04-05T01:05:40.740-07:00NAudio Tutorial 5 - Recording Audio<span lang="en-US">Time for another installment of the NAudio Tutorials; this week we will be looking at how to Record Audio using NAudio from two different recording scenarios. The first being the use of NAudio to record any and all sound coming from the local Sound Card input, whether that be from a Microphone, the Line In Device or the Sound Cards on board wave mixer. The second approach we will be looking at is recording only the Audio that has been mixed by NAudio, regardless of what other audio is being played on the system at the time. This is useful for scenarios where you want to play over a backing track, or play your samples against a click track being played from another program but don't want to record the click track. The additional advantage of recording the audio mixed directly from NAudio is that there is 0 degradation in quality through the process; no audio playing means pure silence, rather than almost silence which for your average audio hardware would be the result, there is always some level of noise when working with an analog signal.</span><br /> <br /> <span lang="en-US">This NAudio Recording audio tutorial builds upon the concepts presented in previous NAudio Tutorials, if you haven't yet had the opportunity to review them I suggest that you venture there first and resume reading this tutorial after you have understood the basic NAudio concepts.</span><br /> <br /> <a href="http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html"><span lang="en-US">http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html</span></a><br /> <a href="http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html"><span lang="en-US">http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html</span></a><br /> <a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html"><span lang="en-US">http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html</span></a><br /> <a href="http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html"><span lang="en-US">http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html</span></a><br /> <a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html"><span lang="en-US">http://opensebj.blogspot.com/2009/03/naudio-tutorial-4-sample-reversing.html</span></a><br /> <br /> <span lang="en-US">Time for another disclaimer, the second approach discussed here, recording the mix directly from NAudio has been suggested as a feature for inclusion in the main branch. I'm not sure if it fits in to the long term direction for the WaveMixerStream32 class, in any case, the code for these modifications have been included in this Tutorial and thanks to the Open Source nature of NAudio you can make these same changes to an instance of the library for yourself. You can find the specific details of this suggest contained in this forum post:</span><br /> <br /> <a href="http://naudio.codeplex.com/Thread/View.aspx?ThreadId=52296"><span lang="en-US">http://naudio.codeplex.com/Thread/View.aspx?ThreadId=52296</span></a><br /> <br /> <span lang="en-US">If you have any feedback on this tutorial, drop me a line or post a question in the comments section below.</span><br /> <br /> <a href="http://www.evolvingsoftware.com/blogfiles/NAudioTutorial5.zip"><span lang="en-US">Download the full article (AbiWord and RTF Format), example C#.Net Source Code and tutorial program here.</span></a><br /> <br /> <span style="font-size:16pt" lang="en-US">Recoding from the Sound Card</span><br /> <br /> <span lang="en-US">This is remarkably simple to achieve in NAudio, short of having a big red button which we push before it leaves the factory. First step is to setup.. ah forget the steps here is the code:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// WaveIn Streams for recording</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveIn</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> waveInStream;</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveFileWriter</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> writer;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveInStream = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveIn</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">(44100,2);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">writer = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveFileWriter</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">(outputFilename, waveInStream.WaveFormat);</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveInStream.DataAvailable += </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">EventHandler</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"><</span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveInEventArgs</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">>(waveInStream_DataAvailable);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveInStream.StartRecording();</span><br /> <br /> <span lang="en-US">No joke that's almost it. The only interesting thing here that we need to consider it that we have added an EventHandler that needs to be setup to handle data when it's ready to be handed off to the WaveFileWriter:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> waveInStream_DataAvailable(</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveInEventArgs</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> writer.WriteData(e.Buffer, 0, e.BytesRecorded);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span lang="en-US">Er, thats it to start recording. We can stop the recording as such:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveInStream.StopRecording();</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveInStream.Dispose();</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveInStream = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">null</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">writer.Close();</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">writer = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">null</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /> <br /> <span lang="en-US">See it would have been to simple a tutorial if we stopped here but feel free to stop reading and give it a crack. Using this method any audio which isn't muted on you input mixer will be recorded; it's up to you and the windows Mixer API to decide what you want to record, isn't that nice; except that you can't only record Audio from your audio application if there are other applications playing sounds in the background - say you get a call on your VOIP connection right in the middle of the hottest composition ever, or some one PM's you in IRC, or you click around on your PC looking for that cool new sample to load, with all the button clicks and other useless sounds being saved in to your mixed composition - oh no. Lets now look at how this unfortunate situation can be avoided.</span><br /> <br /> <span style="font-size:16pt" lang="en-US">Direct-To-Disk recoding via the NAudio WaveMixerStream32 Class</span><br /> <br /> <span lang="en-US">Now this is slightly more complicated but much more fun and presents you with a superior audio recording (especially on lousy or average audio hard ware, say my PC for instance). We will cover the code which will be required within the calling application first and then secondly review the changes required within the NAudio library, just so we have some comparison in amount of effort required for both approaches.</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer.StreamMixToDisk(outputFilename);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer.StartStreamingToDisk();</span><br /> <br /> <span lang="en-US">Assuming you already have the mixer defined, thats all that is required to start recording. We can pause the streaming to disk by:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer.PauseStreamingToDisk();</span><br /> <br /> <span lang="en-US">Or resume by:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer.ResumeStreamingToDisk();</span><br /> <br /> <span lang="en-US">Finally stopping by:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer.StopStreamingToDisk();</span><span style="font-size:10pt;font-family:'Courier New'" lang="en-US"><br></span><br /> <span lang="en-US">Easy enough but we should cover whats required for this to actually work right? So lets dive in to the modifications in the WaveMixerStream32.cs file and hack till our hearts are content. In the declaration section of the class we need to add the following:</span><br /> <br /> <span style="font-size:10pt;color:#008000" lang="-none-">// Declarations to support the streamToDisk recording methodology</span><br /> <span style="font-size:10pt;color:#0000ff" lang="-none-">private</span><span style="font-size:10pt" lang="-none-"> </span><span style="font-size:10pt;color:#0000ff" lang="-none-">bool</span><span style="font-size:10pt" lang="-none-"> streamToDisk;</span><br /> <span style="font-size:10pt;color:#0000ff" lang="-none-">private</span><span style="font-size:10pt" lang="-none-"> </span><span style="font-size:10pt;color:#0000ff" lang="-none-">string</span><span style="font-size:10pt" lang="-none-"> streamToDiskFileName;</span><br /> <span style="font-size:10pt;color:#2b91af" lang="-none-">WaveFileWriter</span><span style="font-size:10pt" lang="-none-"> writer;</span><br /> <br /> <span lang="en-US">Now we add in the methods that support our calls:</span><br /> <br /> <span style="font-size:10pt;color:#808080" lang="-none-">/</span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">//</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> Starts the Strem To Disk recording if a file name to save the stream to has been setup</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> StartStreamingToDisk()</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (streamToDiskFileName != </span><span style="font-size:10pt;font-family:'Courier New';color:#a31515" lang="-none-">""</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> streamToDisk = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">true</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> Pause's the stream to disk recording (No further blocks are written during the mixing)</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> PauseStreamingToDisk()</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> streamToDisk = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> Resume streaming to disk</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> ResumeStreamingToDisk()</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> streamToDisk = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">true</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> Stop the streaming to disk and clean up</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> StopStreamingToDisk()</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> streamToDisk = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> writer.Close();</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> Setup the StreamMixToDisk file and initalise the WaveFileWriter</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><param name="FileName"></span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">FileName to save the mixed stream</span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></param></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> StreamMixToDisk(</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">string</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> FileName)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> streamToDiskFileName = FileName;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> writer = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveFileWriter</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">(FileName, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">this</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">.WaveFormat);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> Using the final set of data passed through in the overriden read method to also be passed to the WaveFileWriter</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></summary></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><param name="buffer"></span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">Data to be written</span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></param></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><param name="offset"></span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">The Offset, should be 0 as we are taking the mixed data to write and want it all</span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></param></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"><param name="count"></span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">The total count of all the mixed data in the buffer</span><span style="font-size:10pt;font-family:'Courier New';color:#808080" lang="-none-"></param></span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> WriteMixStreamOut(</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[] buffer, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> offset, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> count)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Write the data to the file</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> writer.WriteData(buffer, offset, count);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span lang="en-US">All thats left is the modification to the Read method to pass this data back to the WriteMixStream method. Rather than pasting in the whole read method, even though it may make it look like I've done some extra work, I'll just copy in the last 8 or so lines:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">position += count;</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// If streamToDisk has been enabled the mixed audio will be streamed directly to a wave file, so we need to send the data to the wave file writer</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (streamToDisk)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> WriteMixStreamOut(readBuffer, 0, count);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">return</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> count;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span lang="en-US">Having jammed the check for streaming out to disk, after the final calculation and before the method is exited gives us everything we need to stream to our file. So now we have two methods of recording audio data and you want to know what my favorite part is? </span><br /> <br /> <span lang="en-US">You can actually use </span><span style="font-weight:bold" lang="en-US">both at the same time and get multi-track / multi-channel audio recording</span><span lang="en-US"> on the same machine with a fairly standard sound card!</span><br /> <br /> <span lang="en-US">I normally refrain from using exclamation points but I was actually quite excited when I tested this. It means that some one can be jamming along on say a C# Audio Synthesizer / Beat Box or composition tool like OpenSebJ while another person is singing vocals or playing in a guitar riff through line in. I guess if your really talented you could be doing both at the same time, perhaps signing to the jam is more ilkley - what ever it is it can actually work; you can record both sets of audio separately - because the NAudio Stream-To-Disk method is not actually using your sound card to save the mixed result. Cool, well I think so.</span><br /> <br /> <a href="http://www.evolvingsoftware.com/blogfiles/NAudioTutorial5.zip"><span lang="en-US">Download the example program and have a look for yourself. </span><br /></a><br /> <img src="http://www.evolvingsoftware.com/blogfiles/Tutorial5.png"><br /> <br /> <br /> <br /> <br /> <span style="font-size:16pt" lang="en-US">Conclusion</span><br /> <br /> <span lang="en-US">As pre usual, I've packaged up a copy of the entire article, along with a copy of the example program and source for your consumption. For the modifications required to the NAudio library, I have also copied in to the zip the modified version of WaveMixerStream32.cs for your convenience. Let me know if you have any questions, comments or if your keen to contribute to a project like OpenSebJ.</span><br /> <br /> <span lang="en-US">Until next time, when we look at - well I have actually decided yet. There are two things which are on the list from Tutorial 3 however </span><span lang="en-US">I don't think they are currently the items which are peaking my interest, so lets assume it will most likely be something from the list below:</span><br /> <br /> <ul><br /> <li dir="ltr" style="text-align:left;margin-bottom:0.0000in;margin-top:0.0000in;margin-right:0.0000in"><span lang="en-US"> </span><span lang="en-US">Adding Audio Effects to a Stream</span><span lang="en-US"> </span></li><br /> <li dir="ltr" style="text-align:left;margin-bottom:0.0000in;margin-top:0.0000in;margin-right:0.0000in"><span lang="en-US"> </span><span lang="en-US">Transposing the frequency of the stream being played back</span></li><br /> <li dir="ltr" style="text-align:left;margin-bottom:0.0000in;margin-top:0.0000in;margin-right:0.0000in"><span lang="en-US"> Using MIDI to trigger audio samples</span></li><br /> <li dir="ltr" style="text-align:left;margin-bottom:0.0000in;margin-top:0.0000in;margin-right:0.0000in"><span lang="en-US"> Playing compressed Audio (MP3 & OGG)</span></li><br /> <li dir="ltr" style="text-align:left;margin-bottom:0.0000in;margin-top:0.0000in;margin-right:0.0000in"><span lang="en-US"> Or something else that takes my fancy, write to me and suggest what that may be.</span> </li><br /> </ul><br /> <br /> <span lang="en-US">If you haven't already; </span><a href="http://www.evolvingsoftware.com/blogfiles/NAudioTutorial5.zip"><span lang="en-US">Download the full article (AbiWord and RTF Format), example C#.Net Source Code and tutorial program here.</span></a>OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com25tag:blogger.com,1999:blog-20861261.post-80141066141820989422009-03-28T23:46:00.001-07:002009-03-28T23:47:55.057-07:00NAudio Tutorial 4 - Sample Reversing<span lang="en-US">Welcome to the next edition of the NAudio Tutorials series. In this tutorial we will be looking at how a sample can be reversed and played back.</span><br /> <br /> <span lang="en-US">This tutorial builds upon the previous tutorials, if you haven't had a chance to review them I suggest that you read them first before attempting this tutorial:</span><br /> <br /> <a href="http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html"><span lang="en-US">http://opensebj.blogspot.com/2009/02/introduction-to-using-naudio.html</span></a><br /> <a href="http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html"><span lang="en-US">http://opensebj.blogspot.com/2009/02/naudio-tutorial-2-mixing-multiple-wave.html</span></a><br /> <a href="http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html"><span lang="en-US">http://opensebj.blogspot.com/2009/03/naudio-tutorial-3-sample-properties.html</span></a><br /> <a href="http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html"><span lang="en-US">http://opensebj.blogspot.com/2009/03/naudio-tutorials-minor-note.html</span></a><br /> <br /> <span lang="en-US">A bit of a disclaimer for this approach; we will be overriding the Read method to achieve the playback of the reversed sample. Please don't misinterpret this approach to be the most suitable for implementation or the approach which is close to an appropriate design pattern. Marks suggestion for implementing this feature was to create a WaveStreamReverse stream derived from WaveStream. I've taken this approach because I am looking to demonstrate how wave data stored in a byte array can be manipulated and passed back to the mixer for playback.</span><br /> <br /> <span lang="en-US">Also note that during the writing of this tutorial I have uncovered what looks to be a minor bug that was preventing this function for working on samples longer than a second. A complete post of the details is available here: </span><a href="http://naudio.codeplex.com/Thread/View.aspx?ThreadId=50867"><span lang="en-US">http://naudio.codeplex.com/Thread/View.aspx?ThreadId=50867</span></a><br /> <span lang="en-US">Due to this I have packaged a modified version of the NAudio DLL with this Tutorial as per the reference of suggested modification in the NAudio thread.</span><br /> <br /> <span lang="en-US">Hopefully that is suitable for the readership in the crowd out there but if you want to see it setup in a derived WaveStreamReverse class then drop me a line and let me know.</span><br /> <br /> <span lang="en-US"><a href="http://www.evolvingsoftware.com/blogfiles/NAudioTutorial4.zip">You can download a complete copy of all of the source files and this documentation in AbiWord format from here.</a></span><br /> <br /> <br /> <span style="font-size:16pt" lang="en-US">Reversing The Sample</span><br /> <br /> <span lang="en-US">Lets open the floor to how we can actually reverse a wave file. Basically a wave file is made up of samples, for argument sake we will consider a wave file with a two channels (stereo) and the data for both channels, for the same position is considered a sample. A simple example below:</span><br /> <br /> <img alt="Sine Wave Points" src="http://www.evolvingsoftware.com/blogfiles/sinewavePoints.png"><br /> <br /> <span lang="en-US">Here we have a single channel sine wave. The 6 boxes highlight six points in this sine wave, these are samples. Now consider an exact copy of this image for our second channel of audio and we would have two points for each sample. Conceptually thats certainly simple enough; now we need to discuss how this data is stored.</span><br /> <br /> <span lang="en-US">In a wave file the beginning of the file has a set of data explaining what the format of the file is, Frequency, Bit Size, Number of Channels etc. Once we go past this header information the main wave file starts. Based on the presiding information we can determine how to read the wave file. NAudio completes that read and load operation for us (thankfully because if you don't have to look at it, don't, it's not lots of fun) and provides us with a byte array of the actual wave form data. Now depending on the preceding information we need to adjust the way we consider and utalise this data; if we have only a single channel the byte array needs to be read in accordance with that, if we have a greater precision of samples (16 bit vs. 8 bit) then we need to also take that in to account. So specifically for reversing we are interested in the number of bytes per sample, which is calculated as such:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">bytesPerSample = (channelStream.WaveFormat.BitsPerSample / 8) * channelStream.WaveFormat.Channels;</span><br /> <br /> <span lang="en-US">Taking the number of bits per sample and dividing by 8 gives us the number of bytes per sample (which is very important considering that the wave file is stored in a byte array) and then we multiple this by the number of channels of data we need to handle. So in effect, only considering the number of bytes per sample and then reversing the order that the complete sample appears in a byte array allows us to reverse the complete sample.</span><br /> <br /> <span lang="en-US">Lets have a look at how that works, in a new class called NAudioBufferReverse, which takes in the sampleToReverse as a byte array, the length of the source file in bytes and the number of bytes per sample, notice for this class as long as we have pre-calculated the number of bytes per sample we don't actually need to know the details of why there are that many bytes per sample, only that there is and that each sample needs to be moved as a whole, in the reverse order, to another byte array.</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">class</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioBufferReverse</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Length of the buffer</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> numOfBytes;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// The byte array to store the reversed sample</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[] reversedSample;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[] reverseSample(</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[] sampleToReverse, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> SourceLengthBytes, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> bytesPerSample)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> numOfBytes = SourceLengthBytes;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Set the byte array to the length of the source sample</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> reversedSample = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[SourceLengthBytes];</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// The alternatve location; starts at the end and works to the begining</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> b = 0;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">//Prime the loop by 'reducing' the numOfBytes by the first increment for the first sample </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> numOfBytes = numOfBytes - bytesPerSample;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Used for the imbeded loop to move the complete sample</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> q = 0;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Moves through the stream based on each sample</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">for</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> i = 0; i < numOfBytes - bytesPerSample; i = i + bytesPerSample)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Effectively a mirroing process; b will equal i (or be out by one if its an equal buffer)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// when the middle of the buffer is reached.</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> b = numOfBytes - bytesPerSample - i;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Copies the 'sample' in whole to the opposite end of the reversedSample</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">for</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (q = 0; q <= bytesPerSample; q++)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> reversedSample[b + q] = sampleToReverse[i + q];</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Sends back the reversed stream</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">return</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> reversedSample;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span lang="en-US">Yes, over commented if anything but remember this is a tutorial so you can learn whats going on right? After this class has been implemented we now have an available function to help us reverse a byte array by a sample. </span><br /> <br /> <span style="font-size:16pt" lang="en-US">Setup the Sample</span><br /> <br /> <span lang="en-US">So now we have to call this and setup our sample class, which we have introduced in previous tutorials, to access this function. Enter stage right:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// SampleArray to be store the reveresed array</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[] reversedSample;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> _sampleReversed = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">; </span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> AudioSample(</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">string</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> fileName)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> _fileName = fileName;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveFileReader</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> reader = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveFileReader</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">(fileName);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> channelStream = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveChannel32</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">(reader);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> muted = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> volume = 1.0f;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Reverse the sample</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioBufferReverse</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> nbr = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioBufferReverse</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">();</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Setup a byte array which will store the reversed sample, ready for playback</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> reversedSample = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[(</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">)channelStream.Length];</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Read the channelStream sample in to the reversedSample byte array</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> channelStream.Read(reversedSample, 0, (</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">)channelStream.Length);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Calculate how many bytes are used per sample, whole samples are swaped in </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// positioning by the reverse class</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> bytesPerSample = (channelStream.WaveFormat.BitsPerSample / 8) * channelStream.WaveFormat.Channels;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Pass in the byte array storing a copy of the sample, and save back to the</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// reversedSample byte array</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> reversedSample = nbr.reverseSample(reversedSample, (</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">)channelStream.Length, bytesPerSample);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span lang="en-US">So the main difference here is that we have an additional byte array in our sample class, which will be used to store the reversedSample. We cheat a bit by doing this because we don't have to setup the wave format of the reversed sample, as it will be in exactly the same format as the sample which also means there is no requirement to setup any header information, the land of the byte array bliss.</span><br /> <br /> <span style="font-size:16pt" lang="en-US">Read What?</span><br /> <br /> <span lang="en-US">Thats all well and good but now we need to be able to use this reversed sample during wave play back and how do you suppose that some additional wave bytes are going to help us achieve this, well like the good little supper hero NAudio is, it fly's in stage left and flapping under it's giant red cape is a Read method which has the override directive and ties us back to the actual stream reading function. Those astute people in the audience who have read the previous tutorials and committed every word to memory should be nodding like the good bobble heads they are, recalling that we used a similar approach in the previous tutorial to provide looping functionality to our samples. An in rides our override:</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">override</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> Read(</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">[] buffer, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> offset, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> count)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (_sampleReversed)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">//need to understand why this is a more reliable offset</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> offset = (</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">)channelStream.Position;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Have to work out our own number. The only time this number should be </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// different is when we hit the end of the stream but we always need to</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// report that we read the same amount. Missing data is filled in with </span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// silence</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> outCount = count;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Find out if we are trying to read more data than is available in the buffer</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (offset + count > reversedSample.Length)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// If we are then reduce the read amount</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> count = count - ((offset + count) - reversedSample.Length);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">for</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (</span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> i = 0; i < count; i++)</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Individually copy the samples into the buffer for reading by the overriden method</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> buffer[i] = reversedSample[i + offset];</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Setting this position lets us keep track of how much has been played back.</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// There is no other offset used to track this information</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> channelStream.Position = channelStream.Position + count;</span><br /> <br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Regardless of how much is read the count expected by the calling method is</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// the same number as was origionaly provided to the Read method</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">return</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> outCount;</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">else</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Normal read code, sample has not been set to loop</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">return</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> channelStream.Read(buffer, offset, count);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span lang="en-US">Whats going on again? Well we check if the sample has been requested to be played in reverse (every time the read method is called) and then work out from where we should be playing back the reversed sample - at the moment this code is just assuming you play it in one direction from start-to-end or end-to-start; it's not handling the transposing of position for the stream when a reversed flag is set mid playback - that's a small addition for another day.</span><br /> <br /> <span lang="en-US">Now the actual trick here comes by way of us not using the channelStream.Read method when we are using the reverse flag for playback, instead we just write directly to the byte buffer, the samples which are ready for playback. Notice if the stream is not reversed then there is no need to do this, we let it go on using the channelStream.Read method as it always has. So why does this work, well instead of us relying on a standard stream method to read back the data we just copy in data we deem necessary and because this data is in the same format (remember all the reversal occurs after the channelStream has been created) we don't need to do any conversion on the byte array. There was a few little oddities this deals with, like always saying we read as much as was requested even if we didn't but if we don't we throw an exception somewhere else, Nice.</span><br /> <br /> <span lang="en-US">Thats actually about it for this tutorial. I haven't linked this back to the form inside the text here but you can download the sample project and have a look at how it all hangs together (there is really nothing new going on there except for a reverse check box and I don't want to insult any ones intelligence by explaining how that works here). There are two little omissions from this tutorial which I will leave you as homework (that I'll undoubtedly have to do for OpenSebJ at some point in time) </span><br /> <span lang="en-US">1. Looping of the reversed sample for playback</span><br /> <span lang="en-US">2. Transposing playback position so a dynamic switching between reversed and non-reversed sample playback can be handled.</span><br /> <br /> <span style="font-size:16pt" lang="en-US">Conclusion</span><br /> <br /> <span lang="en-US">Thats a wrap for this Tutorial on how to Reverse a Wave Sample in C# using the NAudio Framework. <a href="http://www.evolvingsoftware.com/blogfiles/NAudioTutorial4.zip">All of this content and the example project is available for download from here.</a> Let me know how you go and where you use it, always interested in hearing about Audio C# Development. </span><br /> <br /> <span lang="en-US">Until next time, when we look at recording wave files, direct to disk.</span>OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com3tag:blogger.com,1999:blog-20861261.post-47096251750333527222009-03-23T13:03:00.000-07:002009-03-23T13:19:43.578-07:00NAudio Tutorials - A minor noteIn the 2 & 3 NAudio tutorials, you may have noticed that sample playback seems fast, almost twice the speed of the sample playback as you would probably otherwise expect. I've traced this issue down and it seems to stem from how and when additional samples are added to the mixer and when the wave device is initialised. <br /><br />The code in tutorial 2 & 3 followed this similar line:<br /><br /><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="en-US">/</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">/Setup the Mixer</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveMixerStream32</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">();</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer.AutoStop = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">;</span><br /><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> (waveOutDevice == </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">null</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">)</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">{</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="en-US"> </span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">//waveOutDevice = new AsioOut();</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="en-US"> </span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveOutDevice = </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:10pt;font-family:'Courier New';color:#2b91af" lang="-none-">WaveOut</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">(0, 300, </span><span style="font-size:10pt;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">);</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="-none-"> </span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="en-US"> </span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveOutDevice.Init(mixer);</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="en-US"> </span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveOutDevice.Play();</span><br /><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">}</span><br /><br />And after the sample was loaded we add it to the mixer.<br /><br /><span style="font-size:10pt;font-family:'Courier New'" lang="en-US">m</span><span style="font-size:10pt;font-family:'Courier New'" lang="-none-">ixer.AddInputStream(Sample);</span><br /><br />However adding the sample to the mixer, which in turn has been used when the waveOutDevice has been initiated seems to be the cause of this issue.<br /><br />If the last line, when the Mixer.AddInputStream(Sample) is replaced with the following code, then playback is completed at the normal, expected speed:<br /><br /><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">/</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="en-US">/</span><span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-"> Need to dispose the waveOutDevice - if it is created before the sample has been added</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// to the mixer then sample playback is undertaken at twice the speed for some reason</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveOutDevice.Dispose();</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">// Add the stream to the mixer</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">mixer.AddInputStream(Sample);</span><br /> <span style="font-size:10pt;font-family:'Courier New';color:#008000" lang="-none-">//Re-initalise the waveOutDevice and play back sound</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveOutDevice.Init(mixer);</span><br /> <span style="font-size:10pt;font-family:'Courier New'" lang="-none-">waveOutDevice.Play();</span><br /><br />Try that out and give me a yell if you have any problems..<br /><br />Next NAudio tutorial instalment coming soon; Reversed Sample Playback.OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-20154337251558393342009-03-13T20:18:00.000-07:002009-03-15T04:26:13.170-07:00An ASIO Journey on Windows XP x64 BitI've started a new journey with Audio on Windows since finding NAudio, which has support for ASIO. It's become an even more interesting journey due to my decision to use Windows Professional XP x64 (The 64 bit edition); why exactly I use this has become a bit lost on me. I originally had grandiose plans of buying a gob load of memory but this only turned out to be 4 gig, of which more than 3 gig could be address by a 32 bit version (x86) of Windows any way - perhaps there was no greater reason, than because I could. Regardless of this I always seem to find myself in a slight predicament, many applications and hardware drivers still aren't x64 compatible. <br /><br />See this ASIO love affair of mine, all boils down to the promise of low latency Audio mixing and manipulation, which for OpenSebJ is the Mecca of Audio Development - because none of the audio steamed to the sound card is buffered in advance, so any latency that exists is immediately obvious to the user. My PC has an inbuilt sound card, which has been more than sufficient for my Audio needs - however I got the idea that having a sound card with ASIO drivers included out of the box must be better, or at the very least a low latency Sound Card. Since all I have at the moment is on-board sound (Realtek High Definition Audio), I suspected a dusty old Sound Blaster Live! could produce better results.<br /><br />Finding 64 Bit drivers, for Windows XP Professional x64 was remarkably easy, a simple matter of looking up Creative's website and navigating to the end-of-life products and downloading a provided driver. After installing I went to test out if the driver included ASIO and firing up the NAudio demo application I could select a Creative ASIO device, all looked good until I went to play the sample and was presented with this lovely error message. <br /><br /><img src="http://www.evolvingsoftware.com/blogfiles/ctasio.jpg"><br /><br />Ahh, reassuring isn't it. They went to the hassle of adding an entry for the ASIO API to find but instead of supporting it, a box is popped to let you know how lucky you are to have purchased a Creative product with future support *uhum*. Well not to be too easily swayed, I went looking for alternatives and stumbled across <a href="http://www.kxproject.com/">The kX Project</a> which is a third party WDM audio driver for EMU10K1 and EMU10K2-based sound cards (SoundBlaster Live! and SoundBlaster Audigy 1/2/4 cards) and yes, it supports ASIO. Amazing, however as you can probably guess form my surprise, no 64 bit versions of Windows are supported.<br /><br />So back to where I was, except with another Sound Card in my system, not to be too dismayed I thought I would give <a href="http://www.asio4all.com/">ASIO4ALL</a> a shot with the Creative Sound Blaster Live! and see how much 'better' it performs over the on-board sound. I currently use <a href="http://www.asio4all.com/">ASIO4ALL</a> with the Realtek High Definition Audio on a Gigabyte mother board and the Audio Quality is good, with very few pop's and click's at a sample latency at 480 samples. <br /><br />Here are some example audio files, they are all of a single Piano key but as you can hear the Sound Blaster example is dramatically different when using the ASIO driver. Much louder than the others but no volume setting was changed between the recording of the WaveOut version and the ASIO instance. Compared to the Realtek, which in my opinion, is much more consistent: <br /><br /><a href="http://www.evolvingsoftware.com/blogfiles/Realtek-WaveOut-100ms.mp3">Realtek WaveOut, Latency 100ms</a><br /><a href="http://www.evolvingsoftware.com/blogfiles/Realtek-ASIO4ALL-480Samples.mp3">Realtek ASIO, Latency 480 Samples</a><br /><a href="http://www.evolvingsoftware.com/blogfiles/SoundBlasterLive-WaveOut-100ms.mp3">Sound Blaster Live! WaveOut, Latency 100ms</a><br /><a href="http://www.evolvingsoftware.com/blogfiles/SoundBlasterLive-ASIO4ALL-480Samples.mp3">Sound Blaster Live! ASIO, Latency 480 Samples</a><br /><br />Although not the result I was looking for, which was optimistic at best, I haven't removed the old Sound Blaster yet. But since this hasn't lead me to my goal, I have since started doing some research for Sound Cards in the $50-$200 AUS range. This is budget central and I'm comfortable with that. <br /><br />I've found a few cards that fit in this sort of price point, the general consumer range:<br /><br /><b>Behringer : External USB - UCA222</b><br />According to the documentation in the driver download on their site, the ASIO drivers are only for a 32 bit version of windows; they actually suggest downloading ASIO4ALL for 64 bit versions of Windows. Shame, looks good otherwise at an attractive price point.<br /><a href="http://www.behringer.com/EN/Products/UCA222.aspx">http://www.behringer.com/EN/Products/UCA222.aspx</a><br />AUS Stores:<br /><a href="http://www.djwarehouse.com.au/cat/index.cgi/shopfront/view_product_details?category_id=22641&product_id=1197137">http://www.djwarehouse.com.au/cat/index.cgi/shopfront/view_product_details?category_id=22641&product_id=1197137</a> - $ 70.00 <br /><a href="http://www.storedj.com.au/products/product.php?id=2272">http://www.storedj.com.au/products/product.php?id=2272</a> - $79.00<br /><br /><b>Lexicon: External USB - Recording Interface Alpha</b><br />Product documentation and information looks great but I haven't been able to see any mention of Windows Professional x64 - leaves me doubting it supports it but I will send a request to find out.<br /><a href="http://www.lexiconpro.com/ProductDetails.aspx?ProductID=7">http://www.lexiconpro.com/ProductDetails.aspx?ProductID=7</a><br />AUS Stores:<br /><a href="http://www.djwarehouse.com.au/cat/index.cgi/shopfront/view_product_details?category_id=7912&product_id=206881">http://www.djwarehouse.com.au/cat/index.cgi/shopfront/view_product_details?category_id=7912&product_id=206881</a> - $ 210.00 <br /><a href="http://www.storedj.com.au/products/product.php?id=1149">http://www.storedj.com.au/products/product.php?id=1149</a> - $269.00<br /><br /><b>E-MU's 0202 USB 2.0 Audio Interface</b><br />The E-MU is the only one in this price range which I have seen that explicitly states it supports Windows Professional x64. Warrants more investigation.<br /><a href="http://www.emu.com/products/product.asp?category=610&subcategory=611&product=15186">http://www.emu.com/products/product.asp?category=610&subcategory=611&product=15186</a><br />AUS Stores:<br /><a href="http://www.storedj.com.au/products/product.php?id=1977">http://www.storedj.com.au/products/product.php?id=1977</a> - $259.00<br /><a href="http://bavasmusic.com.au/store/emu-0202-usb-20-p-2989.html">http://bavasmusic.com.au/store/emu-0202-usb-20-p-2989.html</a> - $188.81<br /><a href="http://www.velvetsystems.com/E-MU.0202.USB.Pro-Audio.Interface.cfm">http://www.velvetsystems.com/E-MU.0202.USB.Pro-Audio.Interface.cfm</a> - $219<br /><br />Needless to say some more investigation of these products is required. I found that there are a lot more DJ Stores selling this sort of equipment in Australia than I originally thought; wish there were some better resources to find out all about them.<br /><br />Also in my searching I did come across another generic ASIO driver <a href="http://www.asio2ks.de/details.shtml">ASIO2KS</a> - it didn't work for me and proved a bit of an issue to fully remove but it may work for you. It had an issue with NAudio, trying to match the sample rate - some where between the two of them they couldn't agree and the end result was not good on this system. <br /><br />I'll keep looking at what the best solution for me may be but in the mean time this MIDI keyboard caught my eye - may be the one to convert me and may be the first MIDI keyboard I own. More research required ;-)<br /><br /><b>Behringer U-CONTROL UMX25 Midi Keyboard</b><br />Looks like it packs a bang for the buck. Not sure about x64 support but at this price I may be convinced to run Windows XP 32 bit edition again.<br /><a href="http://www.behringer.com/EN/Products/UMX25.aspx">http://www.behringer.com/EN/Products/UMX25.aspx</a><br />AUS Stores:<br /><a href="http://www.djwarehouse.com.au/cat/index.cgi/shopfront/view_product_details?category_id=22642&product_id=733793">http://www.djwarehouse.com.au/cat/index.cgi/shopfront/view_product_details?category_id=22642&product_id=733793</a> - $ 155.00 <br /><a href="http://www.storedj.com.au/products/product.php?id=912">http://www.storedj.com.au/products/product.php?id=912</a> - $199.00OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com2tag:blogger.com,1999:blog-20861261.post-46860004092390082912009-03-07T16:42:00.000-08:002009-03-07T16:52:05.131-08:00NAudio Tutorial 3 - Sample Properties<span style="font-size:100%;">The 3rd installment in this NAudio tutorial series will be focusing on controlling sample properties. </span><br /><br /><a href="http://www.evolvingsoftware.com/blogfiles/NAudioTutorial3.zip">As this 3rd Instalment in the NAudio Tutorial's, is quite large and there is a fair bit of code to review, I have packaged up the entire tutorial, including an RTF and AbiWord version of this tutorial in a convenient zip for you. Available here.</a><br /> <br /> <span style="font-size:100%;">Additionally we will also look at building the starting blocks of an NAudioInterface class to provide us with a generic level of abstraction that can be called through other applications. This component is specifically to start addressing my own personal needs for the use of the NAudio API, as a replacement to the existing audio API's OpenSebJ is currently using - SDL.Net, OpenAL.Net and previously Direct Sound through Microsoft's Managed DirectX.</span><br /> <br /> <span style="font-size:100%;">The goals for the NAudioInterface we will be building in this tutorial will be to:</span><br /> <ul><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Provide a single method for all a default configuration and setup of audio output; working on the basis that all audio samples are designated for mixing together</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Abstract all of the wave file and stream loading operations, such that only a file name needs to be provided</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Setup a sample concept, such that all audio sample operations can occur through this instance. Such as:</span><br /> <ul><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Play</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Pause</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Pan</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Volume</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Looping</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Sample Playback Position </span> </li><br /> </ul><br /> </li><br /> </ul><br /> <br /> <span style="font-size:100%;">We will be building upon the first two Tutorials, so if you haven't had a chance to review what's going on, they would be a good place to start. </span><br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">NAudio & the relationship to NAudioInterface</span><br /> <br /> <span style="font-size:100%;">NAudio does a great job of abstracting the underlying actions required to load audio files and stream them to various output devices and it does present an admirably well thought out mix of complexity vs. the amount of functionality. So why bother abstracting this any further? </span><br /> <br /> <span style="font-size:100%;">Well in the specific scenario that I'll be presenting here, we will want to reduce some of this complexity and tie the common options and functions together to make interfacing and interacting with the samples more to do with the functions that are being performed and requested and less about the implementation of those functions. So think about the NAudioInterface as the level of abstraction removing us from how its done to just getting it done.</span><br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">Architecture</span><br /> <br /> <span style="font-size:100%;">I'll insert an obligatory diagram, describing the interaction that is going between the various classes and how we are logically grouping these interactions together. </span><br /> <br /> <img style="width:132.3mm" alt="" src="Http://www.evolvingsoftware.com/blogfiles/0.png"><br /> <br /> <span style="font-size:100%;">Note: This diagram is not trying to portray an accurate physical implementation of the classes and the internal workings of NAudio; rather it is designed to depict and explain the logical relationships and their basic level of interaction.</span><br /> <br /> <span style="font-size:100%;">Starting at the top, we have </span><br /> <ul><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Audio Application; This is our Tutorial Application however could be any application which would benefit from utilizing an Interface Structure such as this.</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> NAudioInterface; Groups all of the Audio functionality together, presenting a single unified location for all interaction with the Samples by the Audio Application.</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> WaveMixerStream32; is setup by the NAudioInterface in a Setup method, at this time the WaveMixerStream32 instance is linked to the IWavePlayer. No Changes to this class are made by this Tutorial.</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> IWavePlayer; is our waveOutDevice. In this tutorial we will just initialize an ASIO stream straight to the device. No changes to this class have been made by this Tutorial.</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> AudioSample; Is inherited from the WaveStream class and is the place where the non core NAudio sample related functionality is stored and used to manipulate the data that is read in to the byte array passed through to the WaveMixerStream32. I.e. Setting up a sample to loop.</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> WaveStream; Used in an overrider relationship from the AudioSample instance, to send the byte arrays which are to be mixed by the WaveMixerStream32 and subsequently played y the sound card. No changes to this class have been made in this Tutorial.</span> </li><br /> </ul><br /> <br /> <span style="font-size:100%;">As we constrain all of the related NAudio modifications to the AudioSample class, which is inherited from the WaveStream Class and create the new NAudioInterface class, we can isolate any modifications we need to make in relation to the Audio that is playing in specific locations that mirror the functionality they need to invoke with out directly updating the NAudio libraries. So no branch of the underlying NAudio API required. </span><br /> <br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">NAudioInterface</span><br /> <br /> <span style="font-size:100%;">As we will want to access these functions from multiple locations within our program and for a scenario where there are many windows, from many different classes. We also need to keep consistency from what we are requesting. As such the first step is to declare this as a static class, such that it can be accessed without being instantiated, any where within our program.</span><br /> <br /> <span style="font-size:10px;font-family:'Courier New';color:#0000ff" style="font-size:100%;">u</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">sing NAudio.Wave;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">namespace AudioInterface</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">class</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <br /> <span style="font-size:100%;">& then declare all of the items we will need to keep state information about globally. I.e. we will only need one waveOutDevice and one mixer, which all audio steams will be mixed to. </span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> /</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">/Declarations required for audio out and mixing</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">IWavePlayer</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> waveOutDevice;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveMixerStream32</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> mixer;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// The Sample array we will load our Audio Samples in to</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">AudioSample</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">[] Sample;</span><br /> <br /> <span style="font-size:100%;">The new introduction here, from the previous tutorials, is the AudioSample class. I previously mentioned in Tutorial 2, how there was a really good implementation in one of the NAudio Demo Applications called, MixDiff - specifically in MixDiffStream.cs which provides a great level of abstraction from the heavy lifting and that class has been reused in this tutorial and expanded to add the additional functionality mentioned in the goals. </span><br /> <br /> <span style="font-size:100%;">Conceptually this has been grouped together in to the AudioSample class, such that any interaction with a sample from the calling program should be able to access any sample related functionality through the Sample instance we have created.</span><br /> <br /> <span style="font-size:100%;">Now we have an array of AudioSamples, we need to initiate our waveOutDevice. This is handled in a single method called SetupAudio in the NAudioInterface class and is designed to just be called once when the calling program is setting up all of it's requirements.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#808080" style="font-size:100%;"> /</span><span style="font-size:100%;font-family:'Courier New';color:#808080" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#808080" lang="-none-"><summary></span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-"> Setup Audio via NAudio. </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">Defaults to using Asio for Audio Output.</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#808080" lang="-none-">///</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#808080" lang="-none-"></summary></span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> SetupAudio(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Samples)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//Setup the Mixer</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> mixer = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveMixerStream32</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> mixer.AutoStop = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (waveOutDevice == </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">null</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> waveOutDevice = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">AsioOut</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> waveOutDevice.Init(mixer);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> waveOutDevice.Play();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">AudioSample</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">[Samples];</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">After the waveOutDevice has been setup and the mixer associated, we will now start loading waves in to the AudioSample instance. As such we have a simplified method to handle loading of the samples, funnily enough named LoadSample.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> LoadSample(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">string</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> fileName, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber] = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">AudioSample</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">(fileName);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> mixer.AddInputStream(Sample[sampleNumber]);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;">T</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">he stop is required because when an InputStream </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">is added, if it is too long it will start </span><br /> <span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> // </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">playing because we do not turn off the mixer.</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;">T</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">his is effectively just a work around by making</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">sure that we move the playback position </span><br /> <span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> // </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">to the end of the stream to aviod this issue.</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Stop(sampleNumber);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">Fairly straight forward but two things to note. First, we are adding the sample which has been loaded, to the mixer input stream. Now it's important to remember that we have already associated the mixer input stream with the waveOutDevice and set the device to be in a status of playing. This can result in the wave file being played mid way through when enough data has been loaded - you wouldn't notice it for a small wave sample being loaded but certainly noticeable in a longer length sample. As such we will call the method Stop, lets have a look at this now.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Stop(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Set the position at the end of the sample length</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].Position = Sample[sampleNumber].Length;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">OK, so why have we set the playing position of the Sample to the length of the sample to stop playing the sample? It comes back to our structure of the mixer and the waveOutDevice being a constant state of Play. Therfore we need to make sure that we stop streaming audio from the Sample to the mixer but rather than unloading the sample, moving the current position to the end of the sample ensures that there is no more data is being loaded in to the mixer stream for play back. Similarly or perhaps conversely we have the Play method.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Play(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].Position = 0;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">It becomes just a simple matter of setting the current position for the sample back to the beginning. So now that we have established that there is a fairly easy way to stop the Sample playback we therefore have everything required to construct a Pause() and a Resume() method.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Pause(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].Pause();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Resume(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].Resume();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">I would agree that these two methods don't have a lot of detail - so let's have a sneak peak in to the AudioSample class to see whats going on:</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" style="font-size:100%;">n</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">amespace</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> AudioInterface</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">class</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">AudioSample</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> : </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveStream</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// General Sample Settings (Info)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">string</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _fileName = </span><span style="font-size:100%;font-family:'Courier New';color:#a31515" lang="-none-">""</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _loop;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">long</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _pausePosition = -1;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _pauseLoop;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Sample WaveStream Settings</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveOffsetStream</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> offsetStream;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveChannel32</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> channelSteam;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> muted;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> volume;</span><br /> <br /> <span style="font-weight:bold;font-family:'Courier New'" style="font-size:100%;"><Snip></span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Pause()</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Store the current stream settings</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _pausePosition = Position;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _pauseLoop = _loop;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;">E</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">nsure the sample is temporairly </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">not looped and set the position to the </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">end of the stream</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _loop = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Position = Length;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">Set the loop status back, so that any </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">further modifications of the loop </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">status are observed</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _loop = _pauseLoop;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Resume()</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">Ensure that the sample had actuall been</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">paused and that we are not just jumping </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">to a random position</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (_pausePosition >= 0)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">Set the position of the stream </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New'" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">back to where it was paused</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Position = _pausePosition;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">Set the pause position to negative </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New'" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">so that we know the sample is not currently paused</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _pausePosition = -1;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">If you have been keeping up with me so far, I'll assume this doesn't need more explanation than whats already included in the comments. But for clarity or obsessive compulsive explanations I'll just confirm that we effectively stop the sample the same way we did previously on the Pause and keep a reference to what the position was before stopping. Then on the calling of the resume method we just let it go and start streaming again from the previous position. There are a few other functions from NAudioInterface we will introduce before moving in to the detail of the implementation of these functions in the AudioSample class. These are rather self explanatory and as they are in the NAudioInterface class they contain very little implementation details for whats actually going on under the hood.</span><br /> <br /> <span style="font-size:100%;">The looping will, loop the sample. When it reaches the end of playback, as determined by there being no more audio to add in form the end of the stream, the reading is looped back to the beginning and the process starts over again.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Loop(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Loop)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].SetLoop(Loop);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">Set Pan - Left to Right. We are using a float value that goes from -1.0 to 0 being the center and 1.0 being the complete other end of the speaker pan.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> SetPan(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> pan)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].SetPan(pan);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">We set the volume here, again using a float. The spectrum goes from 0 to 1. </span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> SetVolume(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> volume)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].Volume = volume;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">Setting the position of the sample playback is certainly a concept previously introduced.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">internal</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> SetPostion(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">long</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> position)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].Position = position;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">& Finally wrapping it all together is the get length method for the audio. Now for clarity this is returning in the same format as the SetPosition uses, which is based on the Length of the Source Stream / the number of Bytes Per Sample. i.e. the number of individual samples.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">internal</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">static</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">long</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> GetLength(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleNumber)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">return</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Sample[sampleNumber].Length;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">Well that wraps up what the NAudioInterface looks like. Time to get on to the AudioSample class and all it's related methods. Harrar.</span><br /> <br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">AudioSample</span><br /> <br /> <span style="font-size:100%;">As mentioned earlier, this class is inherited from the WaveStream class, where most of the underlying functionality will still exist and in this scenario very little has been overridden in the AudioSample class.</span><br /> <br /> <span style="font-size:100%;">Although we have already had a sneak peak at how this class is setup it's worth reviewing the key elements here again:</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" style="font-size:100%;">u</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">sing</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> NAudio.Wave;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">namespace</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> AudioInterface</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">class</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">AudioSample</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> : </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveStream</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// General Sample Settings (Info)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">string</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _fileName = </span><span style="font-size:100%;font-family:'Courier New';color:#a31515" lang="-none-">""</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _loop;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">long</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _pausePosition = -1;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _pauseLoop;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Sample WaveStream Settings</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveOffsetStream</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> offsetStream;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveChannel32</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> channelSteam;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> muted;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> volume;</span><br /> <br /> <span style="font-size:100%;">The AudioSample object now represents contains all the information that relates to the individual samples, which helps us keep a structured NAudioInterface class, not needing to be aware of the individual sample properties unless it is specifically interested. So internally we store whether or not the sample is set up to loop, the filename of the original sample, if the sample is paused & what the pause position was, what the volume for the sample is etc.</span><br /> <br /> <span style="font-size:100%;">In our constructor for the AudioSample class, we only need take in the filename initially and we continue to use our other defaults for the sample until told otherwise.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> AudioSample(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">string</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> fileName)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> _fileName = fileName;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveFileReader</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> reader = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveFileReader</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">(fileName);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> offsetStream = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveOffsetStream</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">(reader);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> channelSteam = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">WaveChannel32</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">(offsetStream);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> muted = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> volume = 1.0f;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">Key to the constructor is setting up the channelStream, based off the WaveFileReader. The Offset stream is used if we want to change the offset of a sample, based on where it starts etc. I haven't looked in to this much yet but thought it may be useful in the future so I've left it in for the time being. Performance overhead for this? I have no idea.</span><br /> <br /> <span style="font-size:100%;">Rather then pull out every override that is practically the same I'll focus on those that are at least non-obvious or have a slightly less straight forward explanation; if you have managed to read this far in to the tutorial let me know - it would nice to know that some one had bothered reading this chapter from the novel that is my Audio Programming Experience.</span><br /> <br /> <span style="font-size:100%;">Actually doing this only leaves me with one method that is worth talking about, which hasn't already been covered, which is the overridden method Read.</span><br /> <br /> <span style="font-size:100%;">Read is the central arterial vein of the WaveStream in NAudio and as such is an interesting and fun place to override and make constructive changes. In this example we have used the override method to introduce looping capability for samples; the code for this looping was ripped from one of the other NAudio Demo's. </span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">override</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> Read(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">byte</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">[] buffer, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> offset, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> count)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Check if the stream has been set to loop</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (_loop)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Looping code taken from NAudio Demo</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> read = 0;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">while</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (read < count)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> required = count - read;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> readThisTime = channelSteam.Read(buffer, offset + read, required);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (readThisTime < required)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> channelSteam.Position = 0;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (channelSteam.Position >= channelSteam.Length)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> channelSteam.Position = 0;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> read += readThisTime;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">return</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> read;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">else</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Normal read code, sample has not been set to loop</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">return</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> channelSteam.Read(buffer, offset, count);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">What is happening here is that the Read method knows how much data is required to be read into the stream, for sending to the Mixer. As such we use this length to read in all the available data to the end of the stream. Once we reach the end of the stream, identified by readThisTime being less than the required data for the read (The WaveChannel32 instance, channelStream, returns an integer on the read event representing the number of bytes actually read) - I'm not sure in this instance, when we have the number of bytes read less than what was expected to be read actually sounds like. i.e. is there a momentary gap of audio but from my testing so far I personally have not been able to distinguish any auditable issues from this.</span><br /> <br /> <span style="font-size:100%;color:#0000ff" lang="-none-">int</span><span style="font-size:10pt" lang="-none-"> readThisTime = channelSteam.Read(buffer, offset + read, required);</span><br /> <br /> <span style="font-size:100%;">Or the other check being that the current Position of the Channel Stream being greater than the complete length of the channel stream, we move back to the first position in the channel stream and start reading the additional data from the beginning of the stream.</span><br /> <br /> <span style="font-size:100%;color:#0000ff" style="font-size:100%;">i</span><span style="font-size:100%;color:#0000ff" lang="-none-">f</span><span style="font-size:10pt" lang="-none-"> (channelSteam.Position >= channelSteam.Length)</span><br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">Audio Application</span><br /> <br /> <span style="font-size:100%;">Time to put a bow on all of this and call it done. I've put together to forms that are going to be used to show of the magic that is NAudio Tutorial 3. The first form is a launcher, that can be used to load different wave samples and allows for the same wave sample to be loaded to multiple positions simultaneously for multi-but-same sample fun.</span><br /> <br /> <br /> <img style="width:95.3mm" alt="" src="Http://www.evolvingsoftware.com/blogfiles/b213b5ee-0b64-11de-8d5c-e537f7387494.png"><br /> <br /> <br /> <span style="font-size:100%;">Brutally boring and beautifully powerful. Check one or a number of boxes and then click Open and find a wave sample to load. Once the sample is loaded a number of windows (based on how many positions you have checked) pop up. </span><br /> <br /> <img style="width:108.7mm" alt="" src="Http://www.evolvingsoftware.com/blogfiles/04c9aa28-0b65-11de-8d5c-e537f7387494.png"><br /> <br /> <span style="font-size:100%;">If you have ever seen OpenSebJ and think this window looks strikingly similar to the properties window thats in OpenSebJ - you wouldn't be wrong. For comparison:</span><br /> <br /> <img style="width:110.2mm" alt="" src="Http://www.evolvingsoftware.com/blogfiles/43cff2ea-0b65-11de-8d5c-e537f7387494.png"><span style="font-size:100%;"> </span><br /> <br /> <span style="font-size:100%;">I've only pasted this here so that you can see how this fits in to the big picture of a Real Time mixing Open Source audio composition tool - OpenSebJ; that and because if I can't bang the drum about OpenSebJ here, then where can I?</span><br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">Sample Properties</span><br /> <br /> <span style="font-size:100%;">So back on track the sample properties window will allow us to control all of the functionality we have bubbled to the surface through our NAudioInterface and as you can see we have a button, switch or slider for all of them. So lets have a look at whats required to do all this:</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" style="font-size:100%;">u</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">sing</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> AudioInterface;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" style="font-size:100%;">n</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">amespace</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> NAudioTutorial3</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">partial</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">class</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">SampleProperties</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> : </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">Form</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> samplePosition = -1;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> SampleProperties(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> SamplePosition)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> samplePosition = SamplePosition;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> InitializeComponent();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">We keep a local variable of the SamplePosition populated so that we know what sample this UI relates to when sending instructions the NAudioInterface. </span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> SampleLauncher_Load(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> trkPosition.Maximum = (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)</span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.GetLength(samplePosition);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">On load, we setup the track bar to have a maximum length the same as the sample, so that when we scroll the slider we move the current play position of the sample - think moving a needle along a record but electronically. Also in terms of this analogy of a scratch it is more like if you actually picked up the needle and then moved it to the exact spot and let it play, or when you drag it back it would be like picking it up multiple times putting it back down and letting it play for a moment and then doing the same. Like a leapfrog effect backwards. I will be looking in to how to reverse a sample at a latter point in time to effectively simulate record scratching.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> cmbPlay_Click(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.Play(samplePosition);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> chkLoop_CheckedChanged(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.Loop(samplePosition, chkLoop.Checked);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> cmbPause_Click(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (cmbPause.Text == </span><span style="font-size:100%;font-family:'Courier New';color:#a31515" lang="-none-">"Pause"</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.Pause(samplePosition);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> cmbPause.Text = </span><span style="font-size:100%;font-family:'Courier New';color:#a31515" lang="-none-">"Resume"</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">else</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.Resume(samplePosition);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> cmbPause.Text = </span><span style="font-size:100%;font-family:'Courier New';color:#a31515" lang="-none-">"Pause"</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">One thing to note with the Pan and Volume change is that the actual class uses a float value of 0 to 1 for the volume and -1 to 1 for the pan. As such I have setup the track values to 0 to 1000 and -1000 to 1000 such that the track bars feel smooth when moving them and thus providing a greater level of precision the the actual adjustments that are occurring.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> trkPan_ValueChanged(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.SetPan(samplePosition, (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)trkPan.Value / (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)1000);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> trkVolume_ValueChanged(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.SetVolume(samplePosition, (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)trkVolume.Value / (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">float</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)1000);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;">Changes the current playing position of the sample in real time. Lots of fun can be had with this - even if I do say so myself.</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> trkPosition_MouseMove(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">MouseEventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (e.Button == </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">MouseButtons</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.Left)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.SetPostion(samplePosition, (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">long</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)trkPosition.Value);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">Sample Launcher</span><br /> <br /> <span style="font-size:100%;">Most of the code here relates to the UI and ensuring we only load a single sample once but I'll throw it in for completeness. Refer to in-line comments for details.</span><br /> <br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" style="font-size:100%;">u</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">sing</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> AudioInterface;</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">namespace</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> NAudioTutorial3</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-">{</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">partial</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">class</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioTutorial3</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> : </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">Form</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// sampleLoaded used to make sure we don't try and load in to the</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// same sample position twice</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">[] sampleLoaded = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">bool</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">[256];</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Our Sample Properties window.</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">SampleProperties</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">[] sampleWindow = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">SampleProperties</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">[256];</span><br /> <br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">public</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> NAudioTutorial3()</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> InitializeComponent();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> NAudioTutorial3_Load(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Setup the NAudioInterface first and only required once </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// for the runtime.</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.SetupAudio(256);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Set all data to inital values.</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">for</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> i = 0; i < 256; i++)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleLoaded[i] = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> chkList.Items.Add(i.ToString(), </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">private</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">void</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> cmbOpen_Click(</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">object</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sender, </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">EventArgs</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> e)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// prompt for file load</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">OpenFileDialog</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> openFileDialog = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">OpenFileDialog</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">();</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> openFileDialog.Filter = </span><span style="font-size:100%;font-family:'Courier New';color:#a31515" lang="-none-">"WAV Files (*.wav)|*.wav"</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (openFileDialog.ShowDialog() == </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">DialogResult</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.OK)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Want to iterate through every possiable sample</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">for</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (</span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">int</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> i = 0; i < 256; i++)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">Check if it has previously </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New'" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">been ticked and not previously loaded</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">if</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> (chkList.GetItemChecked(i) == </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">true</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> && sampleLoaded[i] == </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">)</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">One call to the NAudioInterface to </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New'" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">load the sample</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.LoadSample(openFileDialog.FileName, i);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Setup the sample window and show it</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleWindow[i] = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">new</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">SampleProperties</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">(i);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleWindow[i].Show();</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//</span><span style="font-size:100%;font-family:'Courier New';color:#008000" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">Make sure we don't load the </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New'" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">sample position again</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> chkList.SetItemChecked(i, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> chkList.Items[i] = </span><span style="font-size:100%;font-family:'Courier New';color:#a31515" lang="-none-">"Loaded"</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> sampleLoaded[i] = </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">true</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">;</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">else</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> {</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// Untick any item which was ticked </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New'" style="font-size:100%;"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">to be loaded but has </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">// already been previously loaded</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> chkList.SetItemChecked(i, </span><span style="font-size:100%;font-family:'Courier New';color:#0000ff" lang="-none-">false</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">);</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//end for</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><span style="font-size:100%;font-family:'Courier New';color:#008000" lang="-none-">//end if</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> </span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <br /> <br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-"> }</span><br /> <span style="font-size:100%;font-family:'Courier New'" lang="-none-">}</span><br /> <br /> <span style="font-size:100%;">Thats it!</span><br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">Conclusion</span><br /> <br /> <span style="font-size:100%;">In this tutorial we have run through how to setup an Static Class that will act as a broker for all of the related NAudio functionality, such that the code required in forms and extended classes is minimalist in nature.</span><br /> <br /> <span style="font-size:100%;">Reviewing the tutorial against our original goals we have:</span><br /> <br /> <span style="font-weight:bold" style="font-size:100%;">Simple Audio Setup</span><br /> <span style="font-style:italic" style="font-size:100%;">Provide a single method for all a default configuration and setup of audio output; working on the basis that all audio samples are designated for mixing together</span><br /> <br /> <span style="font-size:100%;">We demonstrated this through a single call: </span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">AudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.SetupAudio(256);</span><br /> <br /> <br /> <span style="font-weight:bold" style="font-size:100%;">Simple File Loading</span><br /> <span style="font-size:100%;"><br>A</span><span style="font-style:italic" style="font-size:100%;">bstract all of the wave file and stream loading operations, such that only a file name needs to be provided</span><span style="font-size:100%;"><br></span><br /> <span style="font-size:100%;">Demonstrated through only one method call required to load a sample from our Audio Application: </span><span style="font-size:10pt" lang="-none-"> </span><br /> <br /> <span style="font-size:100%;font-family:'Courier New';color:#2b91af" lang="-none-">NAudioInterface</span><span style="font-size:100%;font-family:'Courier New'" lang="-none-">.LoadSample(openFileDialog.FileName, i);</span><br /> <br /> <span style="font-weight:bold" style="font-size:100%;">Sample Concept</span><br /> <br /> <span style="font-style:italic" style="font-size:100%;">Setup a sample concept, such that all audio sample operations can occur through this instance. Such as:</span><br /> <ul><br /> <li dir="ltr" style="text-align:left;"><span style="font-style:italic" style="font-size:100%;"> </span><span style="font-style:italic" style="font-size:100%;">Play</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-style:italic" style="font-size:100%;"> Pause</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-style:italic" style="font-size:100%;"> Pan</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-style:italic" style="font-size:100%;"> Volume</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-style:italic" style="font-size:100%;"> Looping</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-style:italic" style="font-size:100%;"> Sample Playback Position </span> </li><br /> </ul><br /> <span style="font-size:100%;"> </span><br /> <span style="font-size:100%;">I wont cut and paste the Sample class but you can check the AudioSample class if you don't believe me.</span><br /> <br /> <span style="font-size:100%;">So we have met all our goals and have a Sample Properties interface I can almost drop in as-is to OpenSebJ, brilliant.</span><br /> <br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">Next Time</span><span style="font-size:100%;"> </span><br /> <br /> <span style="font-size:100%;">There are a few options for the following tutorials and I would be interested in hearing peoples thoughts on what you would like to see covered. Some of the items I am personally interested in are:</span><br /> <ul><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> </span><span style="font-size:100%;">How we can introduce a reverse option to a stream, so that we can start supporting a real-time scratch interface.</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Adding Audio Effects to a Stream</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Transposing the frequency of the stream being played back</span></li><br /> <li dir="ltr" style="text-align:left;"><span style="font-size:100%;"> Setting up stream to disk recording and (hopefully) getting it to work while we are still using ASIO (or in my case ASIO4ALL)</span> </li><br /> </ul><br /> <br /> <span style="font-weight:bold;font-size:16pt" style="font-size:100%;">Final Thoughts </span><br /> <br /> <span style="font-size:100%;">This Tutorial ended up being much longer than I expected and became a way for me to impart my experience with Audio Applications in C#, with generalized concepts and nice framework that I can use in OpenSebJ. </span><br /> <br /> <span style="font-size:100%;">However through this process I have also re-factored the tutorial a few times to make it more universally applicable. Moving the sample properties to a separate class for instance, so that it can be instantiated and used to control many samples and not just one - demonstrating the mixing capability and the number of simultaneous streams that can be handled.</span><br /> <br /> <span style="font-size:100%;">There is still more room for improvement on this, perhaps some of these actions should be made simpler in the NAudioInterface and re-factored in to the AudioSample class but we can do that latter. </span><br /><br /><br /><a href="http://www.evolvingsoftware.com/blogfiles/NAudioTutorial3.zip">Reminder: You can download entire tutorial, including an RTF and AbiWord version of this tutorial, with all of the associated code, in a convenient zip here.</a>OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com2tag:blogger.com,1999:blog-20861261.post-720583567938817122009-02-28T00:15:00.000-08:002009-02-28T01:30:16.340-08:00<div><span style=";font-family:'Arial';font-size:180%;" lang="en-US">NAudio, Tutorial 2, Mixing multiple wave files together in real time.</span><p dir="ltr" style="text-align: left;"><span style=";font-family:'Arial';font-size:130%;" lang="en-US">Introduction:</span></p><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Assuming you have read my last post about NAudio, if you haven't there is still time - here; we will move on to mixing multiple audio files and outputting to a single audio device, which would be your sound card. For the purposes of simplicity this tutorial is going to only focus on 4 samples, although you could extend this to as many as you like following this structure. </span><span style="font-size:100%;"><br /></span> <p dir="ltr" style="text-align: left;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Also please note that this Tutorial is not focusing</span><span style=";font-family:'Arial';font-size:100%;" lang="en-US"> on the most optimal way to implement the complete set of functions for a wave stream in terms of mixing and as such no additional inheritance or polymorphism has been used - the focus of this tutorial is to demonstrate the capabilities present within the NAudio library and what can be done when using these interfaces directly with out additional abstraction. I say this now because I have actually hacked to bits, what looks like a great level of abstraction that Mark has written in his demo application MixDiff - so after you have finished reading this check out the class MixDiffStream.cs for a really useful set of functions grouping many of these control arrays in to a single object.</span></p> <p dir="ltr" style="text-align: left;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Moving on with the Tutorial; I've created </span><span style=";font-family:'Arial';font-size:100%;" lang="en-US">a form with 4 buttons on it. We will load a sample against each of these buttons and then use the buttons to trigger the sample for playback, giving us a form to mix the audio samples together in real time - the start of any great Synthesizer or Beat Box. For those of you not using your imagination today we have:</span></p> <a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Ziytxk6N3oM/SakCaFrHDzI/AAAAAAAAABg/PPWKq2Thneo/s1600-h/461524fc-0567-11de-862e-bf1ffff71a34.png"><img style="cursor: pointer; width: 366px; height: 86px;" src="http://3.bp.blogspot.com/_Ziytxk6N3oM/SakCaFrHDzI/AAAAAAAAABg/PPWKq2Thneo/s400/461524fc-0567-11de-862e-bf1ffff71a34.png" alt="" id="BLOGGER_PHOTO_ID_5307776283055689522" border="0" /></a><p><span style=";font-family:'Arial';font-size:130%;" lang="en-US"><br /></span></p><p><span style=";font-family:'Arial';font-size:130%;" lang="en-US">The Code - Declarations:</span></p><p></p><span style=";font-family:'Arial';font-size:100%;" lang="en-US">I'll assume you have read the first tutorial and already know how to add a reference to NAudio and that you remember you need to setup a using statement:</span><br /><p></p><p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">using</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> NAudio.Wave;</span></p><span style=";font-family:'Arial';font-size:100%;" lang="en-US">We will define the key elements of the API we will be using for this project, within the class. IWavePlayer which you may recall from last time is our waveOutDevice = Sound Card and an instance of WaveMixerStream32, which will handle all of the mixing of the actual wave streams. Like last time we will be using an ASIO device for our waveOutDevice - so make sure you have the ASIO4ALL drivers installed if you don't have a sound card that already supports ASIO (Like me):</span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">namespace</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> NAudioMixTest</span></p><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"> <span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">public</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">partial</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">class</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">frmMixTest</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> : </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">Form</span></p> <span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-">//Declarations required for mixing</span></p> <span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">private</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">IWavePlayer</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> waveOutDevice;</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">private</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveMixerStream32</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> mixer;</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">To reduce explanation latter, I've defined a simple array which will store the file names of the samples we are loading. This will also be used when checking if we have already loaded a sample for that position, by virtue of the absence of a filename.</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="en-US"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-">// File names for the loaded samples</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> private</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">string</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[] sampleLoaded = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">string</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[4];</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">These following settings are the ones which have been included in the abstraction layer I mentioned earlier but for the purpose of demonstration have been striped out for individual specification here. The WaveFileReader is used to load the wave file, resulting in us having access to a stream of data for the wave file. </span></p><br /><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-">// Reader instance for the wave file</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveFileReader</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[] reader = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveFileReader</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[4];</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">We don't directly make any modifications to the WaveOffsetStream in this example but it is required to be passed to the WaveChannel32 instance when being setup. The WaveChannel32 instance is then used to control the properties of the stream we are interested in - it's position.</span></p><span style="font-size:100%;"><br /></span><span style="font-size:100%;"></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-">// Other properties of the stream</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveOffsetStream</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[] offsetStream = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveOffsetStream</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[4];</span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveChannel32</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[] channelSteam = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveChannel32</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">[4];</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:130%;" lang="en-US">The Code - Setup:</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Now that we have finished with the formality of the definitions we need, we can start initializing the components. First is to define the mixer and set that it will not AutoStop playback - this allows us to assume that the status of the Mixer is always "Playing" - otherwise it would automatically stop when all inputs have been read, which would be an issue if we want some silence between beats - think boom . tis . boom . tis etc. </span></p><span style="font-size:100%;"><br /></span><span style="font-size:100%;"></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">The waveOutDevice is then declared on the Form load and set to the status of Play, which means we can start sending audio to our sound card. Again ASIO defaults are being used here, so if it errors for you on this step - get ASIO4ALL.</span></p><span style="font-size:100%;"><br /></span><span style="font-size:100%;"></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">public</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> frmMixTest()</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> InitializeComponent();</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-">//Setup the Mixer</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> mixer = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveMixerStream32</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">();</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">mixer.AutoStop = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">false</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">;</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> }</span></p><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">private</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">void</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> Form1_Load(</span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">object</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> sender, </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">EventArgs</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> e)</span><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">for</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> (</span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">int</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> i = 0; i < 4; i++)</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">sampleLoaded[i] = </span><span style="color: rgb(163, 21, 21);font-family:'Courier New';font-size:100%;" lang="-none-">""</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">;</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> }</span></p><span style="font-size:100%;"></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">if</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> (waveOutDevice == </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">null</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">)</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> waveOutDevice = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">AsioOut</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">();</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">waveOutDevice.Init(mixer);</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> waveOutDevice.Play();</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">}</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> }</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:130%;" lang="en-US">The Code - Load a Sample:</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">We are almost there, believe it or not. I'll cover the loading of a sample now and then show how it all wraps together, skipping over the boring file loader dialog bit. Skip..</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span></p><br /><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">private</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">void</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> loadSample(</span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">int</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> position)</span><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-">// prompt for file load</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">OpenFileDialog</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> openFileDialog = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">OpenFileDialog</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">();</span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"></span><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">openFileDialog.Filter = </span><span style="color: rgb(163, 21, 21);font-family:'Courier New';font-size:100%;" lang="-none-">"WAV Files (*.wav)|*.wav"</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">;</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">if</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> (openFileDialog.ShowDialog() == </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">DialogResult</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">.OK)</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"><br /></span></p><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><br /></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span></p><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Now we pass the FileName from the openFileDialog to the WaveFileReader instance. Which as far as we are concerned has given us a stream for the audio file to be read in to.</span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="en-US"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">reader[position] = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveFileReader</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">(openFileDialog.FileName);</span></p><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Humble reader, I'll be honest with you here, I don't know this inside and out so there is an option to use an WaveOffsetStream but I have also found that it doesn't do anything we need for this actual demo. So if you want to use it you set it up like so and then pass it to WaveChannel32:</span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">offsetStream[position] = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveOffsetStream</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">(reader[position]);</span><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">channelSteam[position] = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveChannel32</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">(offsetStream[position]);</span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Or to be really simple replace those two lines with this one, which just passes the WaveFileReader stream directly to the WaveChannel32 instance, which lets us control the stream as required for this example.</span></p><span style="font-size:100%;"><br /></span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="en-US">c</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">hannelSteam[position] = </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">new</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">WaveChannel32</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">(reader[position]);</span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">After the stream has been defined we need to let the mixer know it exists by using AddInputStream and passing in the WaveChannel32 instance. This only needs to be done once and coincidently because we have set all the statuses to play, this audio file will automatically play when it is added. Then we clean up and store the FileName in the array so we know we have loaded a sample at this position.</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 128, 0);font-family:'Courier New';font-size:100%;" lang="-none-">// You only need to do this once per stream</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">mixer.AddInputStream(channelSteam[position]);</span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">sampleLoaded[position] = openFileDialog.FileName;</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">}</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">}</span></p><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:130%;" lang="en-US">The Code - Tying it all together:</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">You would have noticed that the code in the loadSample method is referring to "position" in the array, that is because we call this from each button click once we know we need to load a sample. I.e.</span></p><span style="font-size:100%;"><br /></span><span style="font-size:100%;"></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="en-US"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">private</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">void</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> cmbSound1_Click(</span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">object</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> sender, </span><span style="color: rgb(43, 145, 175);font-family:'Courier New';font-size:100%;" lang="-none-">EventArgs</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> e)</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">if</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> (sampleLoaded[0] == </span><span style="color: rgb(163, 21, 21);font-family:'Courier New';font-size:100%;" lang="-none-">""</span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">)</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> loadSample(0);</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">}</span><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Else, we have loaded the sample and want to re-trigger the playback, so set the position of the sample to 0 - which is the beginning. </span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-">else</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">{ </span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> channelSteam[0].Position = 0;</span></p><span style="color: rgb(0, 0, 255);font-family:'Courier New';font-size:100%;" lang="-none-"> </span><span style=";font-family:'Courier New';font-size:100%;" lang="-none-">}</span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Courier New';font-size:100%;" lang="-none-"> }</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">& Then effectively the same code for the other 3 buttons, just with the positions set to the incremental position in the array.<br /></span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:130%;" lang="en-US">Conclusion:</span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">This 2nd Tutorial for NAudio has covered loading and mixing samples in real time in a relatively straight forward solution. This functionality almost represents the feature set of audio API functionality used by OpenSebJ currently. Loading Sample, Positioning Samples and Mixing Samples in real time. For next time we will look at covering the additional set of functionality currently supported by other Audio API's - such as setting the volume, pan and looping of the samples being mixed.</span></p><span style="font-size:100%;"><br /></span><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"><span style=";font-family:'Arial';font-size:100%;" lang="en-US">Until next time. </span></p><span style="font-size:100%;"><br /></span><p dir="ltr" style="text-align: left; margin-bottom: 0in; margin-top: 0in; margin-right: 0in;"></p><br /><br /></div>OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com3tag:blogger.com,1999:blog-20861261.post-25326919209075966152009-02-27T12:39:00.000-08:002009-02-27T12:44:10.461-08:00Hip Hop for the Intelectual'sSome call it Nerd Core as if it was some alternate form of hip hop but I still don't know how I feel about being called a Nerd, so let me just introduce it as intellectual Hip Hop - not raping about guns, bling and bitches - makes it a worth while audible experience. <br /><br />Don't know what I'm talking about still? <a href="http://antisocial.propagation.net/%7Ez/or/transfORmed.zip">Check out Optimus Rhyme's new EP - Full for Free Download</a> it's a gloriours selection of beats and rhyme. <br /><br />Find out more about them <a href="http://www.optimusrhyme.com/">here, @ home.</a>OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-31232154977847089912009-02-15T00:36:00.000-08:002009-02-16T04:04:01.162-08:00Introduction to Using NAudio<span style="font-weight: bold;font-family:georgia;font-size:130%;" >About NAudio</span><br />NAudio, is an Open Source* <span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType">audio mixing libary written in C#, for the windows platform. It supports PInvoke methods for WaveOut, ASIO, DirectX and some other functions only available on Vista (</span><span id="ctl00_ctl00_MasterContent_Content_wikiSourceLabel">WASAPI - Windows Vista Core Audio)</span><span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType">. It doesn't currently include abstraction support for PortAudio, OpenAL or GStreamer but here's hoping for some cross platform compatibility in the </span><span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType">future</span><span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType">.<br /><br />The SubVersion repository for NAudio is very clean and easy to distinguish what you may need. My development environment is Windows XP x64 so there was an additional step required for the 64 bit environment, which was to set the platform to build as x86 by default.<br /><br /></span><div style="text-align: left;"><span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType">You can do this by:</span></div><span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType"><br /></span>Clicking Build > Configuration Manager and then change all of the projects platform to x86<br /><span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType"><br />With the platform set, everything was ready to go. I recompiled and had a working x86 version, that runs on the 64 bit OS.<br /><br /><span style="font-weight: bold;font-size:130%;" >Introduction: Playing a Sample</span><br />This post is going to focus on loading an audio sample, playing the file and being able to reset the position of playback for the sample.<br /><br />Start a new project, copy the NAudio.dll file over and add it in as a reference. Then set-up the using statements:<br /><pre class="csharpcode"><br /><span class="kwrd">using</span> NAudio.Wave;<br /><span class="kwrd">using</span> NAudio.CoreAudioApi;</pre><br />In the class, we need to define the items which are going to be visible to all of our methods. We have two declarations we are concerned with in this introduction:<br /><pre class="csharpcode"><span class="kwrd">public</span> <span class="kwrd">partial</span> <span class="kwrd">class</span> NAudioTest : Form<br />{<br />IWavePlayer waveOutDevice;<br />WaveStream mainOutputStream;<br /><span class="kwrd">string</span> fileName = <span class="kwrd">null</span>;</pre><br /><span style="font-weight: bold;">waveOutDevice </span>which is an instance of IWavePlayer is going to be used to define the interface to a device that we will be using to play our audio.<br /><br /><span style="font-weight: bold;">mainOutputStream</span> is used to store the audio sample we load and provides a level of abstraction from the Stream so that we don't need to manually move the stream data around when we want to adjust properties such as position - which allows us to easily seek positions within the sample.<br /><br /><span style="font-weight: bold;">fileName</span> will just store the file name of the wave.<br /><br />With these class declarations in place, the next logical step for us is to setup the device which is going to be used to output the Audio. At an appropriate location (under a button click event etc.) we can now initialise the instance. In this tutorial we are going to use the default available ASIO interface. This may not be suitable for all sound cards, it doesn't work on mine by default - <a href="http://www.asio4all.com/">but thanks to ASIO4ALL it does.</a> Grab a copy of it, it's free and works wonders providing a lowlatency interface for mixing audio on windows.<!-- code formatted by http://manoli.net/csharpformat/ --><br /><pre class="csharpcode"><br /><span class="kwrd">try</span><br />{<br />waveOutDevice = <span class="kwrd">new</span> AsioOut();<br />}<br /><span class="kwrd">catch</span> (Exception driverCreateException)<br />{<br />MessageBox.Show(String.Format(<span class="str">"{0}"</span>, driverCreateException.Message));<br /><span class="kwrd">return</span>;<br />}</pre>The default ASIO device has hopefully been created (<a href="http://www.asio4all.com/">if it didn't work for you have you downloaded ASIO4ALL yet?</a>). Now we have an audio device declared but nothing too exciting to your sense have started to occur - for that we need to load an audio file and we need to find one first. For the purpose of this tutorial you can either assume a fixed location and assign it to the fileName variable or add something similar to the OpenFileDialog snipet below:<!-- code formatted by http://manoli.net/csharpformat/ --><br /><pre class="csharpcode">OpenFileDialog openFileDialog = <span class="kwrd">new</span> OpenFileDialog();<br />openFileDialog.Filter = <span class="str">"Wave Files (*.wav)|*.wav|All Files (*.*)|*.*"</span>;<br />openFileDialog.FilterIndex = 1;<br /><span class="kwrd">if</span> (openFileDialog.ShowDialog() == DialogResult.OK)<br />{<br />fileName = openFileDialog.FileName;<br />}</pre>I'll assume that needs no explanation of its own. This sample now needs to be loaded and stored in to a wave stream, which we have called mainOutputStream. We will use the CreateInputStream method from the NAudio Demo application to complete this task.<!-- code formatted by http://manoli.net/csharpformat/ --><br /><pre class="csharpcode">mainOutputStream = CreateInputStream(fileName);</pre>Passing in the name of the file and having the input stream retuned<!-- code formatted by http://manoli.net/csharpformat/ -->. This doesn't require a lot of understanding if your just looking for the wave file to be loaded and the stream retuned, lets assume that's all you care about from this tutorial.<br /><div class="csharpcode"><pre class="csharpcode"><span class="kwrd">private</span> WaveStream CreateInputStream(<span class="kwrd">string</span> fileName)<br />{<br />WaveChannel32 inputStream;<br /><span class="kwrd">if</span> (fileName.EndsWith(<span class="str">".wav"</span>))<br />{<br />WaveStream readerStream = <span class="kwrd">new</span> WaveFileReader(fileName);<br /><span class="kwrd">if</span> (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm)<br />{<br />readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);<br />readerStream = <span class="kwrd">new</span> BlockAlignReductionStream(readerStream);<br />}<br /><span class="kwrd">if</span> (readerStream.WaveFormat.BitsPerSample != 16)<br />{<br />var format = <span class="kwrd">new</span> WaveFormat(readerStream.WaveFormat.SampleRate,<br /> 16, readerStream.WaveFormat.Channels);<br />readerStream = <span class="kwrd">new</span> WaveFormatConversionStream(format, readerStream);<br />}<br />inputStream = <span class="kwrd">new</span> WaveChannel32(readerStream);<br />}<br /><span class="kwrd">else</span><br />{<br /><span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(<span class="str">"Unsupported extension"</span>);<br />}<br /><span class="kwrd">return</span> inputStream;<br />}</pre></div>Now for assembly of all of the pieces. We have a waveOutDevice defined where we will be sending audio to and a wave file has been loaded in to our mainOutputStream. All that's left is to connect the two and hit play:<br /><!-- code formatted by http://manoli.net/csharpformat/ --><br /><pre class="csharpcode"><span class="kwrd">try</span><br />{<br />waveOutDevice.Init(mainOutputStream);<br />}<br /><span class="kwrd">catch</span> (Exception initException)<br />{<br />MessageBox.Show(String.Format(<span class="str">"{0}"</span>, initException.Message), <span class="str">"Error Initializing Output"</span>);<br /><span class="kwrd">return</span>;<br />}<br />waveOutDevice.Play();</pre>Assuming everything was setup right, you should now hear a wave file being played. <a href="http://www.asio4all.com/">If you have difficulties check ASIO4ALL is installed.</a> And the finishing touch is to reset the wave file to play back from the beginning. Find a friendly on-click button event and add the following:<pre class="csharpcode"><br />mainOutputStream.CurrentTime = TimeSpan.FromSeconds(0);</pre><br />Ahem. Done.<br /><br />Thanks to <a href="http://mark-dot-net.blogspot.com/">Mark Heath</a> for this great library - check out <a href="http://mark-dot-net.blogspot.com/">Mark's Blog Here</a>. All of this code was ripped from the <a href="http://www.codeplex.com/naudio">NAudio Demo Application</a> and is available within the <a href="http://www.codeplex.com/naudio">source code package available on codeplex</a>.<br /><br /><span style="font-weight: bold;font-size:130%;" >Next Time</span><br /><br />I'll be looking at loading multiple wave files simultaneously and how to use the included Mixing functions. Stay tuned.<br /><br />* It is licensed under the <span id="ctl00_ctl00_MasterContent_SubTabMenu_licenseType">Microsoft Public License (Ms-PL), which I personally am not to farmiular with. However according to the FAQ there doesn't seem to be any issues distributing it along with another Open Source application. As I use the GPL I need to look a little further in to the licensing compatability but based on what I have read, I am hopeful for the moment that it is fine. </span><br /></span>OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com7tag:blogger.com,1999:blog-20861261.post-19672833181905018942009-01-10T01:32:00.000-08:002009-01-10T01:36:48.363-08:00Ram Disk for Windows XP x86 & 64Reminds me of the days, when I used to load up a demo of Alien Breed 3d on an A4000 and copy the whole game in to Ram so I could play it.<br /><br />This Ram Disk works under Windows XP 32 and 64 bit versions. You can use it to mount Disk images or just allocate memory space to copy files to.<br /><br />Source Code available on the site as well.OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-26727876734277914612008-01-12T15:28:00.000-08:002008-01-12T16:31:55.392-08:00150,000 Downloads"Oh my Strogg, their going after the data brain" well maybe not but some time over the last few days, OpenSebJ has reached another little milestone in it's history - the 150,000 download mark.<br /><br />So what's been happening, well away in a lovely tree filled mountain, the development team have been hard at work playing to much ET:QW (Enemy Territory Quake Wars) but on the OpenSebJ development front there are the sounds of movement, sort of rustling, like little twigs breaking underfoot. These twigs are momentous in their meaning though as some of the tough work has been going on in the back ground.<br /><br />OpenSebJ and previously Beat It have had a long time using interfaces to Microsoft's DirectX, through various interfaces, more recently using an interface called MDX. That added a weighty requirement to the distribution, sometimes in excess of 250 meg and although this was never distributed with OpenSebJ people still needed to download and install it as all good pre-requisites do.<br /><br />Well those requirements are being shaken off and washed away, don't be mistaken - I haven't forsaken Windows support and I'm not going Mackintosh but I have had a revelation and I am going Open Source. Currently the SVN repository has been split in to 3 branches, a SDL.Net branch, OpenAL.Net and the MDX branch for prosperity (wont have further development). After completing a test migration to SDL.Net, I probably wont be continuing this branch any further. It is functional and does what it does but the lack of direct access to the sound buffers and some of the final control means that a lot of original functionality from OpenSebJ would have to be shelved.<br /><br />So for the time being (until the foreseeable future that is) welcome OpenAL.Net to the OpenSebJ distribution and expect a release shortly.<br /><br />~OpenSebJOpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-91331438568827494872007-12-25T16:10:00.000-08:002007-12-25T17:00:50.575-08:00Not The Best Christmas PresentLike many people around the world, yesterday was spent with family and friends gift giving and much to much eating (the fried rice and turkey was fantastic). <br /><br />Last night though I received an unexpected gift, from windows - a corrupted hard disk. Oh what a joyous event, a little drive that could was now a little drive that couldn't. Well by little I actually mean 500Gig. So after a few attempts at booting windows, Last Known Good, Safe Mode, Safe Mode Command Prompt etc, I then attempted a restore and low and behold the windows recovery option couldn't recognize the installation. Enough to drive your sanity to the limits.<br /><br />I attempted to use a Recovery CD - Linux based and while I could use one tool to see the files in the drive still - or at least a listing, that was about it. GParted realised the disk was corrupt and instead recommended chdsk be run before trying to use GParted again. So cutting an unexciting story short, 16hours latter and chkdsk is still running. It's found a whole heap of problems and is still cracking along, slowly.<br /><br />But here is the real lesson in the story (not the usual back up your stuff one) - avoid hibernate / sleep / standby in windows. Before the crash I had been copying files between two local drives, it had then gone in to standby - when I returned to the PC it was reporting a CRC error in the copy. My first though was a stuffed disk but since watching chkdsk I am thinking again - most of the corruption relates to files which were being copied, the point where windows was crashing relating to the disks or acpi. At the moment it's still a hunch but this MS knowledge base article isn't very reassuring <a href="http://support.microsoft.com/kb/331958">Hard disk may become corrupted when entering standby or hibernation or when writing a memory dump</a><br /><br />Any way heres hoping for a happy ending. I think I have at least another 8 hours of disk checking before I'll know the results. After that I'll be avoiding sleep / hibernation and standby, but thats just me.<br /><br />Hope your technology this Christmas works better for you than it has me ;-)OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-19094421612597861542007-12-22T15:56:00.001-08:002007-12-22T16:38:54.009-08:00Open Source Audio on Windows with OpenAl.Net and Tao.OpenAlI for some reason have a passion for Open Source Audio on Windows. I've got no idea why I don't just cross over and use Linux and its already fantastic collection of Audio programs (<a href="http://www.hydrogen-music.org/">Hydrogen - an advanced drum machine</a>, <a href="http://www.rosegardenmusic.com/">Rosegarden</a><a href="http://www.rosegardenmusic.com/"> - Composition tool with MIDI support</a>, <a href="http://ardour.org/">Ardour - professional audio editing, multi-track audio mixing</a> etc.) but for some reason I just enjoy C# and the IDE so I'm not moving, at least for the time being ;-)<br /><br />Any way to get going with Open Source audio libraries in the .Net world there are a few good choices - so far I've used <a href="http://sourceforge.net/projects/cs-sdl/">SDL.Net</a> and <a href="http://sourceforge.net/projects/openaldotnet/">OpenAl.Net</a> (With <a href="http://www.taoframework.com/project/openal">Tao.OpenAl</a> - it's a dependency for OpenAl.Net and Tao.SDL - it's a dependency for SDL.Net) and have had really good results.<br /><br />Currently <a href="http://sourceforge.net/projects/vscalenotes">OpenSebJ - vScaleNotes, the high quality audio, fully sampled, virtual instrument tool</a>, utalises SDL.Net for all of the sample playback and it will soon be using OpenAl.Net for all of the stream to disk recording.<br /><br />I'm starting down the path of migrating OpenSebJ to Open Source only dependencies, currently OpenSebJ still requires the Managed DirectX libraries to function and as I've spoke of previously, accessibility is a serious issue here - that and the fact that Microsoft aren't further developing the MDX libraries. So a serious port is needed here, hopefully my object encapsulation is going to make this fairly easy ;-) Although I know some functions won't be supported going forward.<br /><br />The fun part about using these libraries is making sure that you can package all of the dependencies in a single installer and make it really easy for people to use, rather than requiring a separate installation - some times though, the documentation can be a little lacking. So here's a little documentation for those looking for the requirements:<br /><br /><span style="font-weight: bold;">OpenAl.Net</span><br />OpenAl.Net requires the Tao.OpenAl framework; the Tao.OpenAl framework does the leg work interop'ing with the OpenAl DLL's.<br /><br />To use OpenAl.Net in your project:<br /><ol><li>Add a dependency linking to OpenAl.Net</li><li>OpenAl.Net has a dependency to Tao.OpenAL but if you load Tao.OpenAl into your project make sure it is linked properly</li><li>Make sure the OpenAl DLL's are accessible, for instance in the output directory. You need alut.dll , OpenAL32.dll & wrap_oal.dll - if you don't have all 3, like if you only have alut.dll you will get an error message from the compiler saying that it can't find alut.dll - you need the other ones even though the compiler error message is wrong - I hate that, any way that's why I'm writing this ;-)</li><li>So in your directory with your program you should have alut.dll , OpenAL32.dll , wrap_oal.dll , OpenALDotNet.dll , Tao.OpenAl.dll , AdvanceMath.dll , csogg.dll , csvorbis.dll</li></ol><span style="font-weight: bold;"><br />SDL.Net</span><br />Similar to OpenAL.Net SDL.Net needs Tao.SDL to do the leg work with the underlying SDL DLL's. SDL.Net is fairly similar to setup but doesn't have audio recording support.<br /><br />To use it in your project:<br /><ol><li>Add a reference to SDL.Net</li><li>SDL.Net has a dependency to Tao.SDL but if you load Tao.SDL into your project make sure it is linked properly</li><li>Make sure the SDL DLL's are available - you need a copy of SDL.dll , SDL_gfx.dll , SDL_mixer.dll - just throw them in the output directory if you don't care about locations ;-)</li><li>Now in the output directory you should have SDL.dll , SDL_gfx.dll , SDL_mixer.dll, SdlDotNet.dll & Tao.Sdl.dll</li></ol><br />The really cool thing is that all of these files can be bundled in the installer. Keep a copy of the original sources, as per the GPL and make sure they are included or accessible. Too easy, well once you know the score any way.<br /><br />Happy Open Source Audio Programing ;-)OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0tag:blogger.com,1999:blog-20861261.post-6857151268055578592007-12-22T14:05:00.000-08:002007-12-22T14:25:54.541-08:0064 Bit Windows with C# Express .Net and 32 Bit DLL'sAn architecture change is always welcome and the 64 Bit world presents new and options for memory management and the like but there is certainly a price to pay for this shift. So far my personal experience with the 64 bit world has been one of mixed emotions, many hours of things not working, spending time researching on the net and looking for obtuse answers that sort of solve our problems.<br /><br />The latest one is trying to develop using C# Express 2008 on Windows XP 64 Bit Edition and then using interoperability with a 32 Bit DLL - OpenAl. For some reason the compiler doesn't realise that the 32 Bit DLL is just that, 32 Bits. By default any .Net application developed will target the platform called "Any CPU" as opposed to a specific architecture. This is cool if your not using native DLL's but is a bit of a pain when you do. You will know that you are getting this problem when you start seeing System.BadImageFormatException popping up. It means that your application is trying to cross the Bit boundary - apparently our gracious OS is a separatist?<br /><br />Any way there seems to be a saving grace, you can do development on a 64 Bit OS and set C# Express to specifically target your application to x86, it's just not obvious. Targeting x86 will mean that your application will still run on a 64 Bit OS but will use WoW (Windows on Windows, which is a compatibility layer to backwards support 32 Bit applications) so you won't get the native use of those 64 Bit Int's but you will be able to use your 32 Bit DLL's.<br /><br />To setup your application to do this you need to<br /><ol><li>G in to the menu option Tools>Options and the in the dialog box tick the box in the bottom right corner to "Show all settings"</li><li>Once the settings expand go to "Projects and Settings" expand it and click on "General"</li><li>Check the option which is called "Show advanced build configurations", then close the dialog box<br /></li><li>Now if you right click on the solution explorer, chose properties</li><li>Goto "Configuration Properties", now you should be able to see the platform drop down. This will probably have only "Any CPU" selected; if so click on "Configuration Manager"</li><li>Chose the option "New" under the "Active Solution Platform" drop down box</li><li>Chose the new platform of x86 and copy your settings from "Any CPU"</li><li>Then just make sure that your projects in your solution refer to x86 as the platform rather that "Any CPU"</li></ol>Done.OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com9tag:blogger.com,1999:blog-20861261.post-57803547035400577862007-11-03T01:30:00.000-07:002007-12-14T13:00:26.409-08:00Frameworks & AccessibilityThere's something relaxing about a well formed framework, whether it be a clean simple design, a stylish approach to a new problem being solved, the thought process that went in to its production or the functionality it delivers - what ever the case is something attractive and strangely sensual about good design.<br /><br />Having a strong and simple foundation is important when introducing new people to a team, or a product to an audience and taking this in to account at the start of a project helps present options for future growth.<br /><br />I've been spending some time building a new framework for OpenSebJ; to be used in both the Mixing and vScaleNotes products. I wanted something that would provide a new forging ground and one that could be easily modified and built upon over time, there were some great lessons learned from the OpenSebJ releases so far and a key one is accessibility.<br /><br />It relates to User Interface Design, Application Prerequisites and well formed Frameworks. If the UI is to hard to come to grips with then its not an accessible product for people. If the Application Prerequisites are too much then you immediately reduce your potential install base; unless you can bundle it all in and package it up in the installer - this can be a bit of an issue if your trying to mix incompatible licenses. These issues prevent people accessing and using the product, a similar risk is also present for additional developers. It's important to cultivate the development community and as such it's a great idea to have the lowest barrier to entry for future changes and modifications, which can be facilitated through a well formed accessible framework.<br /><br />Theres also something strangely relaxing about building them..OpenSebJhttp://www.blogger.com/profile/05952655132453138298noreply@blogger.com0