Rules
+ {
+ get { return rules; }
+ }
+ }
+
+ class NamingRule
+ {
+ string regex;
+ string replacement;
+
+ public NamingRule(string regex, string replacement)
+ {
+ this.regex = regex;
+ this.replacement = replacement;
+ }
+
+ public string Regex
+ {
+ get { return regex; }
+ }
+
+ public string Replacement
+ {
+ get { return replacement; }
+ }
+
+
+ }
+}
diff --git a/MidiFileConverter/NamingRules.xml b/MidiFileConverter/NamingRules.xml
new file mode 100644
index 00000000..306ba3ac
--- /dev/null
+++ b/MidiFileConverter/NamingRules.xml
@@ -0,0 +1,76 @@
+
+
+
+ ^[A-Z]\@
+ 3
+ -
+
+
+ ^[0-9A-Z]+\@[0-9][0-9]\.
+
+
+
+ ^[0-9A-Z]+\@
+
+
+
+ _
+
+
+
+ #
+ /
+
+
+ GROOVE
+ G
+
+
+ FILL
+ F
+
+
+ Fill
+ F
+
+
+ POP/ROCK
+ P/R
+
+
+ FUNK/ROCK
+ F/R
+
+
+ BALLAD
+ BLD
+
+
+ ^FILLS
+
+
+
+ FILLS
+ F
+
+
+ MOTOWN
+ MTN
+
+
+ SIDESTICK
+ STK
+
+
+ EZX COCKTAIL
+ Cocktail
+
+
+ SAMBA
+
+
+
+ BAIAO
+
+
+
\ No newline at end of file
diff --git a/MidiFileConverter/OutputMidiType.cs b/MidiFileConverter/OutputMidiType.cs
new file mode 100644
index 00000000..bd5d180b
--- /dev/null
+++ b/MidiFileConverter/OutputMidiType.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MarkHeath.MidiUtils
+{
+ enum OutputMidiType
+ {
+ LeaveUnchanged,
+ Type0,
+ Type1
+ }
+}
diff --git a/MidiFileConverter/Program.cs b/MidiFileConverter/Program.cs
new file mode 100644
index 00000000..bdc95af7
--- /dev/null
+++ b/MidiFileConverter/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace MarkHeath.MidiUtils
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MainForm());
+ }
+ }
+}
\ No newline at end of file
diff --git a/MidiFileConverter/ProgressEventArgs.cs b/MidiFileConverter/ProgressEventArgs.cs
new file mode 100644
index 00000000..74beb53e
--- /dev/null
+++ b/MidiFileConverter/ProgressEventArgs.cs
@@ -0,0 +1,81 @@
+using System;
+
+namespace MarkHeath.MidiUtils
+{
+ ///
+ /// Progress Event Arguments
+ ///
+ public class ProgressEventArgs : EventArgs
+ {
+ private string message;
+ private ProgressMessageType messageType;
+
+ ///
+ /// New progress event arguments
+ ///
+ /// The message type
+ /// The message
+ public ProgressEventArgs(ProgressMessageType messageType, string message)
+ {
+ this.message = message;
+ this.messageType = messageType;
+ }
+
+ ///
+ /// New progress event arguments
+ ///
+ /// The message type
+ /// the message format string
+ /// format arguments
+ public ProgressEventArgs(ProgressMessageType messageType, string message, params object[] args)
+ {
+ this.messageType = messageType;
+ this.message = String.Format(message, args);
+ }
+
+ ///
+ /// The message
+ ///
+ public string Message
+ {
+ get
+ {
+ return message;
+ }
+ }
+
+ ///
+ /// The message type
+ ///
+ public ProgressMessageType MessageType
+ {
+ get
+ {
+ return messageType;
+ }
+ }
+ }
+
+ ///
+ /// Progress Message Type
+ ///
+ public enum ProgressMessageType
+ {
+ ///
+ /// Trace
+ ///
+ Trace,
+ ///
+ /// Information
+ ///
+ Information,
+ ///
+ /// Warning
+ ///
+ Warning,
+ ///
+ /// Error
+ ///
+ Error,
+ }
+}
diff --git a/MidiFileConverter/Properties/AssemblyInfo.cs b/MidiFileConverter/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..1452e8bb
--- /dev/null
+++ b/MidiFileConverter/Properties/AssemblyInfo.cs
@@ -0,0 +1,89 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MIDI File Converter")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Mark Heath")]
+[assembly: AssemblyProduct("MIDI File Converter")]
+[assembly: AssemblyCopyright("Copyright © Mark Heath 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("c25a6f6b-abf0-4460-a49e-d73c069f80e3")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("0.3.10.0")]
+[assembly: AssemblyFileVersion("0.3.10.0")]
+
+// build 1 29 Oct 2006
+// build 1 is experimental
+// updated for Groove Monkee loops so it works on all incoming files
+// build 2 30 Oct 2006
+// setting to leave MIDI file type unchanged
+// settings to force notes to a specific channel
+// added clip naming option
+// uses new generic about box
+// uses new generic progress log
+// decoupled logic from Main Form UI
+// option not to rename files at all
+// Hidden option to trim text markers and remove blank
+// Hidden option to insert a name marker and delete all others
+// Hidden option to recreate end track markers
+// basic install script
+// should now be able to cope with multi-track type 1s as input as well
+// show hourglass
+// remove empty tracks option added
+// settings upgrade option
+// Update help file
+// build 3
+// fixed a bug removing blank tracks
+// build 4
+// advanced options dialog
+// clear log on start
+// build 5 31 Oct 2006
+// Remove extra tempo events option
+// Remove extra markers option
+// build 6 31 Oct 2006
+// option to save conversion log
+// build 7 2 Nov 2006
+// final build for release
+// build 8 3 Nov 2006
+// minor changes
+// build 9 6 Mar 2007
+// renamed to MIDI file converter
+// now hosted on CodePlex
+// fixed a bug where track 1 didn't have an end track marker if Recreate Track End Markers wasn't set,
+// and converting from type 0 to type 1
+// build 10 5 Apr 2007
+// updated to use new MidiEventCollection
+
+// revamp help for advanced options
+
+// Next version
+// support changing note length
+// perhaps allow markers less than final note event
+// work out times in measures and beats
+// review error handling
+// Consider a command line interface
+// Selecting what to copy & what to process (somehow)
+// Protect against output folder being a subfolder of input folder
+
+// Testing
+// Public release
\ No newline at end of file
diff --git a/MidiFileConverter/Properties/Resources.Designer.cs b/MidiFileConverter/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..455e7cd2
--- /dev/null
+++ b/MidiFileConverter/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.17929
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace MarkHeath.MidiUtils.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MarkHeath.MidiUtils.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/MidiFileConverter/Properties/Resources.resx b/MidiFileConverter/Properties/Resources.resx
new file mode 100644
index 00000000..ffecec85
--- /dev/null
+++ b/MidiFileConverter/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/MidiFileConverter/Properties/Settings.Designer.cs b/MidiFileConverter/Properties/Settings.Designer.cs
new file mode 100644
index 00000000..c6ae9b8e
--- /dev/null
+++ b/MidiFileConverter/Properties/Settings.Designer.cs
@@ -0,0 +1,218 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.17929
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace MarkHeath.MidiUtils.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string InputFolder {
+ get {
+ return ((string)(this["InputFolder"]));
+ }
+ set {
+ this["InputFolder"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string OutputFolder {
+ get {
+ return ((string)(this["OutputFolder"]));
+ }
+ set {
+ this["OutputFolder"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool ApplyNamingRules {
+ get {
+ return ((bool)(this["ApplyNamingRules"]));
+ }
+ set {
+ this["ApplyNamingRules"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool VerboseOutput {
+ get {
+ return ((bool)(this["VerboseOutput"]));
+ }
+ set {
+ this["VerboseOutput"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool RemoveSequencerSpecific {
+ get {
+ return ((bool)(this["RemoveSequencerSpecific"]));
+ }
+ set {
+ this["RemoveSequencerSpecific"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("Type1")]
+ public global::MarkHeath.MidiUtils.OutputMidiType OutputMidiType {
+ get {
+ return ((global::MarkHeath.MidiUtils.OutputMidiType)(this["OutputMidiType"]));
+ }
+ set {
+ this["OutputMidiType"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("-1")]
+ public int OutputChannelNumber {
+ get {
+ return ((int)(this["OutputChannelNumber"]));
+ }
+ set {
+ this["OutputChannelNumber"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool UseFileName {
+ get {
+ return ((bool)(this["UseFileName"]));
+ }
+ set {
+ this["UseFileName"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool TrimTextEvents {
+ get {
+ return ((bool)(this["TrimTextEvents"]));
+ }
+ set {
+ this["TrimTextEvents"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool AddNameMarker {
+ get {
+ return ((bool)(this["AddNameMarker"]));
+ }
+ set {
+ this["AddNameMarker"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool RecreateEndTrackMarkers {
+ get {
+ return ((bool)(this["RecreateEndTrackMarkers"]));
+ }
+ set {
+ this["RecreateEndTrackMarkers"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool RemoveEmptyTracks {
+ get {
+ return ((bool)(this["RemoveEmptyTracks"]));
+ }
+ set {
+ this["RemoveEmptyTracks"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool FirstTime {
+ get {
+ return ((bool)(this["FirstTime"]));
+ }
+ set {
+ this["FirstTime"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string ProductVersion {
+ get {
+ return ((string)(this["ProductVersion"]));
+ }
+ set {
+ this["ProductVersion"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool RemoveExtraTempoEvents {
+ get {
+ return ((bool)(this["RemoveExtraTempoEvents"]));
+ }
+ set {
+ this["RemoveExtraTempoEvents"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool RemoveExtraMarkers {
+ get {
+ return ((bool)(this["RemoveExtraMarkers"]));
+ }
+ set {
+ this["RemoveExtraMarkers"] = value;
+ }
+ }
+ }
+}
diff --git a/MidiFileConverter/Properties/Settings.settings b/MidiFileConverter/Properties/Settings.settings
new file mode 100644
index 00000000..4f321f15
--- /dev/null
+++ b/MidiFileConverter/Properties/Settings.settings
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ Type1
+
+
+ -1
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+
+
+
+ False
+
+
+ False
+
+
+
\ No newline at end of file
diff --git a/MidiFileConverter/app.config b/MidiFileConverter/app.config
new file mode 100644
index 00000000..802eb0a3
--- /dev/null
+++ b/MidiFileConverter/app.config
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+
+
+ True
+
+
+ False
+
+
+ Type1
+
+
+ -1
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+
+
+
+ False
+
+
+ False
+
+
+
+
diff --git a/MidiFileConverter/midi_file_converter.html b/MidiFileConverter/midi_file_converter.html
new file mode 100644
index 00000000..37c32036
--- /dev/null
+++ b/MidiFileConverter/midi_file_converter.html
@@ -0,0 +1,307 @@
+
+
+ MIDI File Converter
+
+
+ MIDI File Converter
+
+ MIDI File Converter is a utility designed to convert the MIDI file library
+ included with Toontrack's EZdrummer virtual
+ instrument from MIDI type 0 to MIDI type 1, with a customisable name as the track
+ 1 name. This has the advantage that users of Cakewalk's
+ SONAR
+ will find that the MIDI clips created when they drag and drop into
+ the track pane are given a meaningful default name. It also supports the option
+ to leave the files as type 0 and simply change the track name, which is supported
+ by SONAR 6 and above.
+
+
+ This utility has now been expanded to alter other MIDI files
+ that you might be using with EZD to adjust their end time markers so that they loop
+ correctly with EZdrummer's built in loop browser. (EZD version 1.0.3 introduced
+ some problems in this area).
+
+ Download
+
+ A link to the latest version of MIDI File converter can be found
+ here .
+
+ Installation and Requirements
+
+ MIDI File Converter requires the .NET framework version 2.0 to be installed.
+ You can download this
+ here . Once you have done this you can run the installer (or download the zip archive,
+ decompress it and run MIDI File Converter.exe ).
+
+ Settings
+
+
+
+ Input Folder
+
+ Normally you would choose the EZdrummer MIDI folder as the input folder. This is
+ typically located at C:\Program Files\Toontrack\EZDrummer\Midi . However, if you
+ want to specifically choose which MIDI files will be processed, you can select a
+ subfolder or a new folder into which you have copied in the exact files you want to be processed.
+
+ Output Folder
+
+ MIDI File Converter will not modify any existing files or folders on your PC.
+ What it does is recreates all the contents of the input folder into an empty output
+ folder you have specified. This folder must be initially blank, and its contents
+ can be copied into the Toontrack MIDI folder when you have finished the conversion
+ (you must do this part yourself).
+
+ Clip Naming
+
+ These settings govern the clip name you will see when dragging into your host sequencer.
+ Please note that in certain circumstances, EZD will generate its own clip name instead
+ of the one in the file. You will need to experiment with your own host sequencer
+ to see how it behaves.
+
+ If the Apply XML Naming Rules to Toontrack EZD Patterns option is selected, then
+ whenever a MIDI file that was supplied with EZdrummer is processed, it will be given
+ a MIDI clip name that is calculated according to the rules in the NamingRules.xml
+ file (see "Advanced Customisation" below for more details). This setting is turned
+ on by default.
+
+ If the Use filename for other MIDI patterns option is selected, then all other
+ MIDI files in the input folder will have their clip name set to the name of the
+ file (minus the .mid extension). This setting is on by default. If you turn it off,
+ MIDI files will retain whatever clip names they already had (which may be blank).
+
+ MIDI Note Channel
+
+ You will normally want to leave MIDI notes on whatever channel they were already
+ on. However, you can move them all to track 1 to be just like the ones in the included
+ EZD libraries. Or you can move them all to track 10, so that when you play them
+ in Windows Media Player, or through a GM module, it will play drum sounds instead
+ of piano sounds.
+
+ Output File Type
+
+ MIDI File Converter was written to convert Toontrack's type 0 files into type
+ 1, so that their clip names would display correctly in SONAR 5. However, users of
+ different hosts may find that they can use type 0 without problems. Choose type
+ 1 to force all MIDI files to type 1, type 0 to force them all to type 0, and leave
+ unchanged if you want them to stay as they are.
+
+ Verbose Output
+
+ Select this option if you want a detailed run-down of everything that EZdrummer
+ MIDI Converter is doing. If it is turned off, you will still be informed of any
+ errors encountered.
+
+ How to make MIDI clip names appear in SONAR
+ Note: these are for users of SONAR 5 or previous versions. SONAR 6 can display
+ EZdrummers own clip names. You may still be able to use this utility with SONAR 6 though
+ if you want to customise what those clip names will be.
+
+ First, make sure you have no running instances of EZdrummer.
+ MIDI File Converter will not modify, delete or rename any files on your computer.
+ It will only create new ones. So now, create an empty folder somewhere to contain
+ the created library. A good example would be on your desktop, in a subfolder called
+ Midi.
+ Make sure that the EZdrummer MIDI folder is pointing at EZdrummer's own MIDI folder.
+ Point the output folder at your desired output path. MIDI File Converter will
+ insist that this is a blank folder, to avoid confusion.
+ You want to select type 1 as the ouput file type.
+ If you have user MIDI files you would like to be given clip names as well then select
+ the Use filename for other MIDI patterns option.
+ By default Apply XML Naming Rules is enabled. If you turn this off, the track name
+ for converted EZdrummer MIDI files will simply be the filename minus the .mid extension.
+ It is possible to extensively customise the naming rules, but this is for advanced
+ users only. See the next section for more details. If you just have the EZdrummer
+ included MIDI files, and no expansion packs, the default naming rules will probably
+ be just fine for you.
+ When you are ready, click Convert. The process will take a few minutes, depending
+ on the speed of your computer.
+ When it has finished, you will be informed of how many files were processed, as
+ well as the number of errors encountered. If you have any errors at all, it is recommended
+ that you report them to mark.heath@gmail.com .
+
+ If you are happy that the output folder contains the MIDI files you want, you are
+ ready to replace EZdrummer's MIDI files with the converted ones. Please make a backup
+ first . One way would simply be to rename the existing MIDI folder to something
+ else (e.g. Original MIDI).
+ Now copy the entire contents of the output directory into the Toontrack MIDI folder
+ (by default this will be C:\Program Files\Toontrack\EZDrummer\Midi .
+
+
+ How to make user MIDI files loop correctly in EZD 1.0.3
+
+ First, copy all the folders of MIDI files that have the looping problem into an
+ empty folder. This will be your input folder.
+ Create another blank folder to use as your output folder.
+ If you would like them to be given MIDI clip names based on their filename at the
+ same time, ensure that "Use filename for other MIDI patterns" is selected.
+ You can also take the opportunity to modify the MIDI file type or the channel number
+ for the notes. This may be helpful depending on your host.
+ You may also wish to turn on some options in the advanced options screen, available
+ on the Tools menu.
+ When you are ready click Convert . If the process completes without errors, you can
+ replace your user MIDI files with those in the output folder.
+
+
+ Advanced Customisation
+
+ MIDI File Converter allows extensive customisation of the filenames it creates.
+ If you are comfortable with editing XML files and using regular expressions, you
+ will be able to customise these settings. This allows the flexibility to support
+ any EZX expansion pack MIDI files you may purchase. The settings are all stored
+ in NamingRules.xml , which should be in the same folder as the EZdrummer MIDI
+ Converter application. Please be careful modifying these settings and always check
+ that the output is what you wanted before replacing your EZdrummer MIDI files.
+
+ General Settings - This section contains some global settings. Do not change
+ the order of the keys in this section.
+ FilenameRegex - This pattern is matched against all the filenames. If a match
+ is found, then MIDI File Converter considers this to be an EZdrummer MIDI file,
+ and will attempt to apply naming rules. The default setting looks for a filename
+ that starts with one uppercase letter followed by an @ symbol. You only need to
+ change this setting if files you wanted naming rules applied on are being missed
+ out, or files you didn't want the naming rules applied on were being included.
+ ContextDepth - This is the number of components (folders and filename) that
+ will be used to formulate the track 1 name. This is normally set to 3 (4 is the
+ maximum), but can be set lower if desired.
+ ContextSeparator - This string will be inserted between the converted name
+ of each item in the context hierarchy. If the naming rules have caused the length
+ of a section to be set to zero, then the context separator is not added. The default
+ will be just fine for most people.
+ Rules - The rules section allows you to specify a number of string substitutions
+ to be applied to each part of the name. This consists of two parts - the SearchString
+ and the Replacement (see below). Each rule is applied in turn on each folder
+ name or filename used to form the track name. The order is very important in this
+ section - the rules will be applied in the order they appear in the NamingRules.xml
+ file.
+ SearchString - this is a regular expression that defines a string to look
+ for within the file or folder name.
+ Replacement - this is a literal string that will replace all occurences of
+ the search string in the file or folder name being processed.
+
+
+ Some example rules from the default NamingRules.xml file:
+ <Rule>
+ <SearchString>^[0-9A-Z]+\@[0-9][0-9]\.</SearchString>
+ <Replacement></Replacement>
+ </Rule>
+
+ This rule looks for all file or folder names that start with one or more digits
+ or upper case letters followed by an @ sign, followed by exactly two digits and
+ then a full stop. It replaces it with a blank string. This effectively strips off
+ the start of EZdrummer folder and filenames.
+ <Rule>
+ <SearchString>POP/ROCK</SearchString>
+ <Replacement>P/R</Replacement>
+ </Rule>
+
+ This rule abbreviates any instances of the upper case string "POP/ROCK" to "P/R".
+ Note that this will only operate if it follows the rule that converts the '#' character
+ to a '/' character.
+ <Rule>
+ <SearchString>GROOVE </SearchString>
+ <Replacement>G</Replacement>
+ </Rule>
+
+ This rule abbreviates any instances of the upper case string "GROOVE" followed by
+ a trailing space to simply "G". Note that this only works because we have already
+ converted all underscore characters to spaces with a previous rule.
+ <Rule>
+ <SearchString>SAMBA</SearchString>
+ <Replacement></Replacement>
+ </Rule>
+
+ The Samba MIDI files included with the Cocktail kit EZX will have the word Samba
+ in their name twice by default. This rule strips out the fully capitalised folder
+ name, so the mixed case "Samba" in the filename can be used on its own.
+
+ Advanced Options
+
+ There are some extra settings available that may be useful for some third party
+ add-on MIDI libraries. These are accessed from the Tools menu. They are recommended
+ for advanced users only. Messages will appear in the output window to inform you
+ of these settings being modified from their default values.
+
+ Remove Sequencer Specific Events - The Toontrack EZD files each have a sequencer
+ specific event in them. I don't know what it is for, but you can remove it if you
+ want (maybe to trick EZD into thinking this is a user file instead). Default is
+ False .
+ Recreate End Track Events - This is what will fix most looping issues in
+ EZD v1.03. It deletes the existing end track markers and puts new ones in that fall
+ exactly after the last note event. Default is True . If this is set to False,
+ then end track markers are left exactly where they were.
+ Add Name Marker - In addition to naming the tracks, this will add a marker
+ to track zero with the name of the clip. Any existing markers at position 0 on track
+ 0 will be removed. Default is False
+ Trim Text Events - Trims the whitespace of Text events and removes them if
+ they are zero length. Default is False
+ Remove Empty Tracks - If the input file is type 1 and has tracks containing
+ no note events, these tracks can be removed. The control track and the first track
+ are never removed. Default is False .
+ Remove Extra Tempo Events - Most MIDI files have just one tempo event on
+ the first tick. This option removes any subsequent ones (this is sometimes needed
+ to fix the looping issue in EZD v1.0.3). Default is False .
+ Remove Extra Markers - This will remove any markers that are not at the first
+ tick. This is sometimes needed to fix the looping issue in EZD v1.0.3. Default is
+ False .
+
+
+ Notes
+
+ This software is not affiliated in any way with Toontrack. It is not guaranteed
+ to work on future versions of EZdrummer, or with EZX expansion packs (although I
+ expect it will work just fine).
+ I recommend that if you are installing any patches or updates to EZdrummer, that
+ you restore the original Midi directory before doing so, and then re-run the EZdrummer
+ MIDI Converter afterwards.
+ If you have some user MIDI patterns that you do not want MIDI File Converter
+ to attempt to convert, simply remove them from the input directory before running
+ the conversion process. You do not have to point it to the real EZdrummer MIDI directory
+ if you don't want to. You could create your own folder with only the files you want
+ converted in.
+ For the technically inclined, here is an explanation of what happens to your MIDI
+ events when converting to type 1. All meta-events are placed on track 0. All note
+ events are placed on track 1. In addition a track name (the one worked out by the
+ naming rules) and a track end marker are added to track 1.
+
+
+ Version History
+
+ The latest version of MIDI File Converter can be found at
+ http://www.codeplex.com/naudio .
+
+
+ v0.1 9 Oct 2006
+
+ First public beta release
+
+
+ v0.2 2 Nov 2006
+
+ Now processes all MIDI files in the input folder, rather than just copying type
+ 1
+ Allow processing of non-EZD files (to move end track markers to work with EZD v1.03)
+ Ability to select output file type
+ Both tracks in a type 1 file will be given the same track name
+ An advanced options dialog added with extra options
+ Can optionally force all MIDI note events onto channel 1 or 10
+ Properly handles non-ASCII characters (such as copyright symbol)
+ Has an installer
+ Can save the conversion log
+
+
+ v0.3 16 Mar 2007
+
+ Name changed to MIDI File Converter to reflect its use for more general-purpose tasks
+ Fixed a bug where track 1 didn't have an end track marker when converting from type 0 to type 1
+
+
+ v0.4 - t.b.a.
+
+ Fixed a sysex writing bug
+ Better preserving of event ordering
+
+
+
+
+
diff --git a/MixDiff/AboutForm.Designer.cs b/MixDiff/AboutForm.Designer.cs
new file mode 100644
index 00000000..bd8cd680
--- /dev/null
+++ b/MixDiff/AboutForm.Designer.cs
@@ -0,0 +1,144 @@
+namespace NAudio.Utils
+{
+ partial class AboutForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.linkLabelWebsite = new System.Windows.Forms.LinkLabel();
+ this.buttonOK = new System.Windows.Forms.Button();
+ this.labelProductName = new System.Windows.Forms.Label();
+ this.labelCopyright = new System.Windows.Forms.Label();
+ this.linkLabelFeedback = new System.Windows.Forms.LinkLabel();
+ this.label3 = new System.Windows.Forms.Label();
+ this.labelVersion = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // linkLabelWebsite
+ //
+ this.linkLabelWebsite.AutoSize = true;
+ this.linkLabelWebsite.Location = new System.Drawing.Point(13, 67);
+ this.linkLabelWebsite.Name = "linkLabelWebsite";
+ this.linkLabelWebsite.Size = new System.Drawing.Size(168, 13);
+ this.linkLabelWebsite.TabIndex = 0;
+ this.linkLabelWebsite.TabStop = true;
+ this.linkLabelWebsite.Text = "http://www.codeplex.com/naudio";
+ this.linkLabelWebsite.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelWebsite_LinkClicked);
+ //
+ // buttonOK
+ //
+ this.buttonOK.Location = new System.Drawing.Point(111, 124);
+ this.buttonOK.Name = "buttonOK";
+ this.buttonOK.Size = new System.Drawing.Size(75, 23);
+ this.buttonOK.TabIndex = 1;
+ this.buttonOK.Text = "OK";
+ this.buttonOK.UseVisualStyleBackColor = true;
+ this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
+ //
+ // labelProductName
+ //
+ this.labelProductName.AutoSize = true;
+ this.labelProductName.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.labelProductName.Location = new System.Drawing.Point(13, 13);
+ this.labelProductName.Name = "labelProductName";
+ this.labelProductName.Size = new System.Drawing.Size(196, 16);
+ this.labelProductName.TabIndex = 2;
+ this.labelProductName.Text = "{Application.ProductName}";
+ //
+ // labelCopyright
+ //
+ this.labelCopyright.AutoSize = true;
+ this.labelCopyright.Location = new System.Drawing.Point(13, 51);
+ this.labelCopyright.Name = "labelCopyright";
+ this.labelCopyright.Size = new System.Drawing.Size(149, 13);
+ this.labelCopyright.TabIndex = 2;
+ this.labelCopyright.Text = "Copyright © Mark Heath 2007";
+ //
+ // linkLabelFeedback
+ //
+ this.linkLabelFeedback.AutoSize = true;
+ this.linkLabelFeedback.Location = new System.Drawing.Point(13, 101);
+ this.linkLabelFeedback.Name = "linkLabelFeedback";
+ this.linkLabelFeedback.Size = new System.Drawing.Size(150, 13);
+ this.linkLabelFeedback.TabIndex = 3;
+ this.linkLabelFeedback.TabStop = true;
+ this.linkLabelFeedback.Text = "mark.heath@gmail.com";
+ this.linkLabelFeedback.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelFeedback_LinkClicked);
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(13, 85);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(243, 13);
+ this.label3.TabIndex = 2;
+ this.label3.Text = "Send feedback, feature requests and bug fixes to:";
+ //
+ // labelVersion
+ //
+ this.labelVersion.AutoSize = true;
+ this.labelVersion.Location = new System.Drawing.Point(13, 33);
+ this.labelVersion.Name = "labelVersion";
+ this.labelVersion.Size = new System.Drawing.Size(93, 13);
+ this.labelVersion.TabIndex = 2;
+ this.labelVersion.Text = "{Version: X.X.X.X}";
+ //
+ // AboutForm
+ //
+ this.AcceptButton = this.buttonOK;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(292, 159);
+ this.Controls.Add(this.linkLabelFeedback);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.labelVersion);
+ this.Controls.Add(this.labelCopyright);
+ this.Controls.Add(this.labelProductName);
+ this.Controls.Add(this.buttonOK);
+ this.Controls.Add(this.linkLabelWebsite);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "AboutForm";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "About {Application.ProductName}";
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.LinkLabel linkLabelWebsite;
+ private System.Windows.Forms.Button buttonOK;
+ private System.Windows.Forms.Label labelProductName;
+ private System.Windows.Forms.Label labelCopyright;
+ private System.Windows.Forms.LinkLabel linkLabelFeedback;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label labelVersion;
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/AboutForm.cs b/MixDiff/AboutForm.cs
new file mode 100644
index 00000000..2d5861e0
--- /dev/null
+++ b/MixDiff/AboutForm.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+
+namespace NAudio.Utils
+{
+ ///
+ /// A standard about form
+ ///
+ public partial class AboutForm : Form
+ {
+ ///
+ /// Creates a new about form
+ ///
+ public AboutForm()
+ {
+ InitializeComponent();
+ labelProductName.Text = Application.ProductName;
+ labelVersion.Text = String.Format("Version: {0}", Application.ProductVersion);
+ this.Text = String.Format("About {0}", Application.ProductName);
+ }
+
+ private void linkLabelWebsite_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ System.Diagnostics.Process.Start(linkLabelWebsite.Text);
+ }
+
+ private void linkLabelFeedback_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
+ {
+ System.Diagnostics.Process.Start("mailto:" + linkLabelFeedback.Text);
+ }
+
+ private void buttonOK_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ ///
+ /// The URL of the website to use for help
+ /// e.g. http://www.codeplex.com/naudio
+ ///
+ public string Url
+ {
+ get { return linkLabelWebsite.Text; }
+ set { linkLabelWebsite.Text = value; }
+ }
+
+ ///
+ /// The email address for feedback
+ ///
+ public string Email
+ {
+ get { return linkLabelFeedback.Text; }
+ set { linkLabelFeedback.Text = value; }
+ }
+
+ ///
+ /// The copyright info
+ /// e.g. Copyright © 2007 Mark Heath
+ ///
+ public string Copyright
+ {
+ get { return labelCopyright.Text; }
+ set { labelCopyright.Text = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/AboutForm.resx b/MixDiff/AboutForm.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/MixDiff/AboutForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/MixDiff/Images.Designer.cs b/MixDiff/Images.Designer.cs
new file mode 100644
index 00000000..8be93219
--- /dev/null
+++ b/MixDiff/Images.Designer.cs
@@ -0,0 +1,143 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.17929
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace MarkHeath.AudioUtils {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Images {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Images() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MarkHeath.AudioUtils.Images", typeof(Images).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Back {
+ get {
+ object obj = ResourceManager.GetObject("Back", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Forward {
+ get {
+ object obj = ResourceManager.GetObject("Forward", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Loop {
+ get {
+ object obj = ResourceManager.GetObject("Loop", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Pause {
+ get {
+ object obj = ResourceManager.GetObject("Pause", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Play {
+ get {
+ object obj = ResourceManager.GetObject("Play", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Rewind {
+ get {
+ object obj = ResourceManager.GetObject("Rewind", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Shuffle {
+ get {
+ object obj = ResourceManager.GetObject("Shuffle", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap Stop {
+ get {
+ object obj = ResourceManager.GetObject("Stop", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+ }
+}
diff --git a/MixDiff/Images.resx b/MixDiff/Images.resx
new file mode 100644
index 00000000..7e225022
--- /dev/null
+++ b/MixDiff/Images.resx
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ Resources\Back.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Forward.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Loop.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Pause.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Play.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Rewind.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Shuffle.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Stop.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/MixDiff/MixDiff.csproj b/MixDiff/MixDiff.csproj
new file mode 100644
index 00000000..71a099b0
--- /dev/null
+++ b/MixDiff/MixDiff.csproj
@@ -0,0 +1,202 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.50727
+ 2.0
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}
+ WinExe
+ Properties
+ MarkHeath.AudioUtils
+ MixDiff
+
+
+
+
+
+
+
+
+
+
+
+
+ 3.5
+ v3.5
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+ Client
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AllRules.ruleset
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AllRules.ruleset
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ AboutForm.cs
+
+
+
+ True
+ True
+ Images.resx
+
+
+ Form
+
+
+ MixDiffForm.cs
+
+
+
+
+ Form
+
+
+ PropertiesForm.cs
+
+
+
+ AboutForm.cs
+ Designer
+
+
+ Designer
+ ResXFileCodeGenerator
+ Images.Designer.cs
+
+
+ Designer
+ MixDiffForm.cs
+
+
+ Designer
+ PropertiesForm.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ Designer
+ SettingsForm.cs
+
+
+ True
+ Resources.resx
+ True
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+ Form
+
+
+ SettingsForm.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}
+ NAudio
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/MixDiff/MixDiff.csproj.vspscc b/MixDiff/MixDiff.csproj.vspscc
new file mode 100644
index 00000000..979cc996
--- /dev/null
+++ b/MixDiff/MixDiff.csproj.vspscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = "relative:MixDiff"
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
+}
diff --git a/MixDiff/MixDiffForm.Designer.cs b/MixDiff/MixDiffForm.Designer.cs
new file mode 100644
index 00000000..266d8373
--- /dev/null
+++ b/MixDiff/MixDiffForm.Designer.cs
@@ -0,0 +1,491 @@
+namespace MarkHeath.AudioUtils
+{
+ partial class MixDiffForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+ this.toolStripButtonPlay = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonPause = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonStop = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonBack = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonForward = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonRewind = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonLoop = new System.Windows.Forms.ToolStripButton();
+ this.toolStripButtonShuffle = new System.Windows.Forms.ToolStripButton();
+ this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
+ this.toolStripLabel1 = new System.Windows.Forms.ToolStripLabel();
+ this.toolStripLabelPosition = new System.Windows.Forms.ToolStripLabel();
+ this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
+ this.toolStripLabel2 = new System.Windows.Forms.ToolStripLabel();
+ this.toolStripLabelLength = new System.Windows.Forms.ToolStripLabel();
+ this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator();
+ this.buttonA = new System.Windows.Forms.Button();
+ this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
+ this.selectFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.clearToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.propertiesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.buttonB = new System.Windows.Forms.Button();
+ this.buttonC = new System.Windows.Forms.Button();
+ this.buttonD = new System.Windows.Forms.Button();
+ this.toolTip1 = new System.Windows.Forms.ToolTip(this.components);
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.menuStrip1 = new System.Windows.Forms.MenuStrip();
+ this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.openSavedComparisonToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.saveComparisonToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.compareModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.currentPositionToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.skipBackToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.restartToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.contentsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStrip1.SuspendLayout();
+ this.contextMenuStrip1.SuspendLayout();
+ this.menuStrip1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // toolStrip1
+ //
+ this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.toolStripButtonPlay,
+ this.toolStripButtonPause,
+ this.toolStripButtonStop,
+ this.toolStripButtonBack,
+ this.toolStripButtonForward,
+ this.toolStripButtonRewind,
+ this.toolStripButtonLoop,
+ this.toolStripButtonShuffle,
+ this.toolStripSeparator1,
+ this.toolStripLabel1,
+ this.toolStripLabelPosition,
+ this.toolStripSeparator2,
+ this.toolStripLabel2,
+ this.toolStripLabelLength,
+ this.toolStripSeparator3});
+ this.toolStrip1.Location = new System.Drawing.Point(0, 24);
+ this.toolStrip1.Name = "toolStrip1";
+ this.toolStrip1.Size = new System.Drawing.Size(642, 25);
+ this.toolStrip1.TabIndex = 7;
+ this.toolStrip1.Text = "toolStrip1";
+ //
+ // toolStripButtonPlay
+ //
+ this.toolStripButtonPlay.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonPlay.Image = global::MarkHeath.AudioUtils.Images.Play;
+ this.toolStripButtonPlay.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonPlay.Name = "toolStripButtonPlay";
+ this.toolStripButtonPlay.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonPlay.Text = "Play";
+ this.toolStripButtonPlay.Click += new System.EventHandler(this.toolStripButtonPlay_Click);
+ //
+ // toolStripButtonPause
+ //
+ this.toolStripButtonPause.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonPause.Image = global::MarkHeath.AudioUtils.Images.Pause;
+ this.toolStripButtonPause.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonPause.Name = "toolStripButtonPause";
+ this.toolStripButtonPause.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonPause.Text = "Pause";
+ this.toolStripButtonPause.Click += new System.EventHandler(this.toolStripButtonPause_Click);
+ //
+ // toolStripButtonStop
+ //
+ this.toolStripButtonStop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonStop.Image = global::MarkHeath.AudioUtils.Images.Stop;
+ this.toolStripButtonStop.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonStop.Name = "toolStripButtonStop";
+ this.toolStripButtonStop.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonStop.Text = "Stop";
+ this.toolStripButtonStop.Click += new System.EventHandler(this.toolStripButtonStop_Click);
+ //
+ // toolStripButtonBack
+ //
+ this.toolStripButtonBack.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonBack.Image = global::MarkHeath.AudioUtils.Images.Back;
+ this.toolStripButtonBack.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonBack.Name = "toolStripButtonBack";
+ this.toolStripButtonBack.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonBack.Text = "Skip Back";
+ this.toolStripButtonBack.Click += new System.EventHandler(this.toolStripButtonBack_Click);
+ //
+ // toolStripButtonForward
+ //
+ this.toolStripButtonForward.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonForward.Image = global::MarkHeath.AudioUtils.Images.Forward;
+ this.toolStripButtonForward.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonForward.Name = "toolStripButtonForward";
+ this.toolStripButtonForward.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonForward.Text = "Skip Forward";
+ this.toolStripButtonForward.Click += new System.EventHandler(this.toolStripButtonForward_Click);
+ //
+ // toolStripButtonRewind
+ //
+ this.toolStripButtonRewind.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonRewind.Image = global::MarkHeath.AudioUtils.Images.Rewind;
+ this.toolStripButtonRewind.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonRewind.Name = "toolStripButtonRewind";
+ this.toolStripButtonRewind.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonRewind.Text = "Rewind";
+ this.toolStripButtonRewind.Click += new System.EventHandler(this.toolStripButtonRewind_Click);
+ //
+ // toolStripButtonLoop
+ //
+ this.toolStripButtonLoop.CheckOnClick = true;
+ this.toolStripButtonLoop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonLoop.Image = global::MarkHeath.AudioUtils.Images.Loop;
+ this.toolStripButtonLoop.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonLoop.Name = "toolStripButtonLoop";
+ this.toolStripButtonLoop.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonLoop.Text = "Loop";
+ //
+ // toolStripButtonShuffle
+ //
+ this.toolStripButtonShuffle.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonShuffle.Image = global::MarkHeath.AudioUtils.Images.Shuffle;
+ this.toolStripButtonShuffle.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonShuffle.Name = "toolStripButtonShuffle";
+ this.toolStripButtonShuffle.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonShuffle.Text = "Shuffle";
+ this.toolStripButtonShuffle.Click += new System.EventHandler(this.toolStripButtonShuffle_Click);
+ //
+ // toolStripSeparator1
+ //
+ this.toolStripSeparator1.Name = "toolStripSeparator1";
+ this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
+ //
+ // toolStripLabel1
+ //
+ this.toolStripLabel1.Name = "toolStripLabel1";
+ this.toolStripLabel1.Size = new System.Drawing.Size(53, 22);
+ this.toolStripLabel1.Text = "Position:";
+ //
+ // toolStripLabelPosition
+ //
+ this.toolStripLabelPosition.Name = "toolStripLabelPosition";
+ this.toolStripLabelPosition.Size = new System.Drawing.Size(70, 22);
+ this.toolStripLabelPosition.Text = "00:00:00.000";
+ this.toolStripLabelPosition.ToolTipText = "Position";
+ //
+ // toolStripSeparator2
+ //
+ this.toolStripSeparator2.Name = "toolStripSeparator2";
+ this.toolStripSeparator2.Size = new System.Drawing.Size(6, 25);
+ //
+ // toolStripLabel2
+ //
+ this.toolStripLabel2.Name = "toolStripLabel2";
+ this.toolStripLabel2.Size = new System.Drawing.Size(47, 22);
+ this.toolStripLabel2.Text = "Length:";
+ //
+ // toolStripLabelLength
+ //
+ this.toolStripLabelLength.Name = "toolStripLabelLength";
+ this.toolStripLabelLength.Size = new System.Drawing.Size(49, 22);
+ this.toolStripLabelLength.Text = "00:00:00";
+ //
+ // toolStripSeparator3
+ //
+ this.toolStripSeparator3.Name = "toolStripSeparator3";
+ this.toolStripSeparator3.Size = new System.Drawing.Size(6, 25);
+ //
+ // buttonA
+ //
+ this.buttonA.ContextMenuStrip = this.contextMenuStrip1;
+ this.buttonA.Font = new System.Drawing.Font("Verdana", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.buttonA.Location = new System.Drawing.Point(12, 62);
+ this.buttonA.Name = "buttonA";
+ this.buttonA.Size = new System.Drawing.Size(150, 150);
+ this.buttonA.TabIndex = 8;
+ this.buttonA.Text = "";
+ this.buttonA.UseVisualStyleBackColor = true;
+ this.buttonA.Click += new System.EventHandler(this.OnMixButtonClick);
+ //
+ // contextMenuStrip1
+ //
+ this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.selectFileToolStripMenuItem,
+ this.clearToolStripMenuItem,
+ this.propertiesToolStripMenuItem});
+ this.contextMenuStrip1.Name = "contextMenuStrip1";
+ this.contextMenuStrip1.Size = new System.Drawing.Size(137, 70);
+ //
+ // selectFileToolStripMenuItem
+ //
+ this.selectFileToolStripMenuItem.Name = "selectFileToolStripMenuItem";
+ this.selectFileToolStripMenuItem.Size = new System.Drawing.Size(136, 22);
+ this.selectFileToolStripMenuItem.Text = "&Select File...";
+ this.selectFileToolStripMenuItem.Click += new System.EventHandler(this.selectFileToolStripMenuItem_Click);
+ //
+ // clearToolStripMenuItem
+ //
+ this.clearToolStripMenuItem.Name = "clearToolStripMenuItem";
+ this.clearToolStripMenuItem.Size = new System.Drawing.Size(136, 22);
+ this.clearToolStripMenuItem.Text = "&Clear";
+ this.clearToolStripMenuItem.Click += new System.EventHandler(this.clearToolStripMenuItem_Click);
+ //
+ // propertiesToolStripMenuItem
+ //
+ this.propertiesToolStripMenuItem.Name = "propertiesToolStripMenuItem";
+ this.propertiesToolStripMenuItem.Size = new System.Drawing.Size(136, 22);
+ this.propertiesToolStripMenuItem.Text = "&Properties...";
+ this.propertiesToolStripMenuItem.Click += new System.EventHandler(this.propertiesToolStripMenuItem_Click);
+ //
+ // buttonB
+ //
+ this.buttonB.ContextMenuStrip = this.contextMenuStrip1;
+ this.buttonB.Font = new System.Drawing.Font("Verdana", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.buttonB.Location = new System.Drawing.Point(168, 62);
+ this.buttonB.Name = "buttonB";
+ this.buttonB.Size = new System.Drawing.Size(150, 150);
+ this.buttonB.TabIndex = 9;
+ this.buttonB.Text = "";
+ this.buttonB.UseVisualStyleBackColor = true;
+ this.buttonB.Click += new System.EventHandler(this.OnMixButtonClick);
+ //
+ // buttonC
+ //
+ this.buttonC.ContextMenuStrip = this.contextMenuStrip1;
+ this.buttonC.Font = new System.Drawing.Font("Verdana", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.buttonC.Location = new System.Drawing.Point(324, 62);
+ this.buttonC.Name = "buttonC";
+ this.buttonC.Size = new System.Drawing.Size(150, 150);
+ this.buttonC.TabIndex = 10;
+ this.buttonC.Text = "";
+ this.buttonC.UseVisualStyleBackColor = true;
+ this.buttonC.Click += new System.EventHandler(this.OnMixButtonClick);
+ //
+ // buttonD
+ //
+ this.buttonD.ContextMenuStrip = this.contextMenuStrip1;
+ this.buttonD.Font = new System.Drawing.Font("Verdana", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.buttonD.Location = new System.Drawing.Point(480, 62);
+ this.buttonD.Name = "buttonD";
+ this.buttonD.Size = new System.Drawing.Size(150, 150);
+ this.buttonD.TabIndex = 11;
+ this.buttonD.Text = "";
+ this.buttonD.UseVisualStyleBackColor = true;
+ this.buttonD.Click += new System.EventHandler(this.OnMixButtonClick);
+ //
+ // timer1
+ //
+ this.timer1.Interval = 200;
+ this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // menuStrip1
+ //
+ this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.fileToolStripMenuItem,
+ this.optionsToolStripMenuItem,
+ this.helpToolStripMenuItem});
+ this.menuStrip1.Location = new System.Drawing.Point(0, 0);
+ this.menuStrip1.Name = "menuStrip1";
+ this.menuStrip1.Size = new System.Drawing.Size(642, 24);
+ this.menuStrip1.TabIndex = 11;
+ this.menuStrip1.Text = "menuStrip1";
+ //
+ // fileToolStripMenuItem
+ //
+ this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.openSavedComparisonToolStripMenuItem,
+ this.saveComparisonToolStripMenuItem,
+ this.exitToolStripMenuItem});
+ this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
+ this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
+ this.fileToolStripMenuItem.Text = "&File";
+ //
+ // openSavedComparisonToolStripMenuItem
+ //
+ this.openSavedComparisonToolStripMenuItem.Name = "openSavedComparisonToolStripMenuItem";
+ this.openSavedComparisonToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.openSavedComparisonToolStripMenuItem.Text = "Open Comparison...";
+ this.openSavedComparisonToolStripMenuItem.Click += new System.EventHandler(this.openSavedComparisonToolStripMenuItem_Click);
+ //
+ // saveComparisonToolStripMenuItem
+ //
+ this.saveComparisonToolStripMenuItem.Name = "saveComparisonToolStripMenuItem";
+ this.saveComparisonToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.saveComparisonToolStripMenuItem.Text = "Save Comparison...";
+ this.saveComparisonToolStripMenuItem.Click += new System.EventHandler(this.saveComparisonToolStripMenuItem_Click);
+ //
+ // exitToolStripMenuItem
+ //
+ this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+ this.exitToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
+ this.exitToolStripMenuItem.Text = "E&xit";
+ this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
+ //
+ // optionsToolStripMenuItem
+ //
+ this.optionsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.compareModeToolStripMenuItem,
+ this.settingsToolStripMenuItem});
+ this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
+ this.optionsToolStripMenuItem.Size = new System.Drawing.Size(61, 20);
+ this.optionsToolStripMenuItem.Text = "&Options";
+ //
+ // compareModeToolStripMenuItem
+ //
+ this.compareModeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.currentPositionToolStripMenuItem1,
+ this.skipBackToolStripMenuItem1,
+ this.restartToolStripMenuItem1});
+ this.compareModeToolStripMenuItem.Name = "compareModeToolStripMenuItem";
+ this.compareModeToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
+ this.compareModeToolStripMenuItem.Text = "&Compare Mode";
+ //
+ // currentPositionToolStripMenuItem1
+ //
+ this.currentPositionToolStripMenuItem1.Checked = true;
+ this.currentPositionToolStripMenuItem1.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.currentPositionToolStripMenuItem1.Name = "currentPositionToolStripMenuItem1";
+ this.currentPositionToolStripMenuItem1.Size = new System.Drawing.Size(160, 22);
+ this.currentPositionToolStripMenuItem1.Text = "&Current Position";
+ this.currentPositionToolStripMenuItem1.Click += new System.EventHandler(this.currentPositionToolStripMenuItem1_Click);
+ //
+ // skipBackToolStripMenuItem1
+ //
+ this.skipBackToolStripMenuItem1.Name = "skipBackToolStripMenuItem1";
+ this.skipBackToolStripMenuItem1.Size = new System.Drawing.Size(160, 22);
+ this.skipBackToolStripMenuItem1.Text = "&Skip Back";
+ this.skipBackToolStripMenuItem1.Click += new System.EventHandler(this.skipBackToolStripMenuItem1_Click);
+ //
+ // restartToolStripMenuItem1
+ //
+ this.restartToolStripMenuItem1.Name = "restartToolStripMenuItem1";
+ this.restartToolStripMenuItem1.Size = new System.Drawing.Size(160, 22);
+ this.restartToolStripMenuItem1.Text = "&Restart";
+ this.restartToolStripMenuItem1.Click += new System.EventHandler(this.restartToolStripMenuItem1_Click);
+ //
+ // settingsToolStripMenuItem
+ //
+ this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
+ this.settingsToolStripMenuItem.Size = new System.Drawing.Size(157, 22);
+ this.settingsToolStripMenuItem.Text = "&Settings...";
+ this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click);
+ //
+ // helpToolStripMenuItem
+ //
+ this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.contentsToolStripMenuItem,
+ this.aboutToolStripMenuItem});
+ this.helpToolStripMenuItem.Name = "helpToolStripMenuItem";
+ this.helpToolStripMenuItem.Size = new System.Drawing.Size(44, 20);
+ this.helpToolStripMenuItem.Text = "&Help";
+ //
+ // contentsToolStripMenuItem
+ //
+ this.contentsToolStripMenuItem.Name = "contentsToolStripMenuItem";
+ this.contentsToolStripMenuItem.Size = new System.Drawing.Size(122, 22);
+ this.contentsToolStripMenuItem.Text = "&Contents";
+ this.contentsToolStripMenuItem.Click += new System.EventHandler(this.contentsToolStripMenuItem_Click);
+ //
+ // aboutToolStripMenuItem
+ //
+ this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
+ this.aboutToolStripMenuItem.Size = new System.Drawing.Size(122, 22);
+ this.aboutToolStripMenuItem.Text = "&About";
+ this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click);
+ //
+ // MixDiffForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(642, 296);
+ this.Controls.Add(this.buttonD);
+ this.Controls.Add(this.toolStrip1);
+ this.Controls.Add(this.buttonC);
+ this.Controls.Add(this.buttonB);
+ this.Controls.Add(this.buttonA);
+ this.Controls.Add(this.menuStrip1);
+ this.KeyPreview = true;
+ this.MainMenuStrip = this.menuStrip1;
+ this.Name = "MixDiffForm";
+ this.Text = "MixDiff";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MixDiffForm_FormClosing);
+ this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MixDiffForm_KeyDown);
+ this.toolStrip1.ResumeLayout(false);
+ this.toolStrip1.PerformLayout();
+ this.contextMenuStrip1.ResumeLayout(false);
+ this.menuStrip1.ResumeLayout(false);
+ this.menuStrip1.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ToolStrip toolStrip1;
+ private System.Windows.Forms.ToolStripButton toolStripButtonPlay;
+ private System.Windows.Forms.ToolStripButton toolStripButtonPause;
+ private System.Windows.Forms.ToolStripButton toolStripButtonStop;
+ private System.Windows.Forms.ToolStripButton toolStripButtonBack;
+ private System.Windows.Forms.ToolStripButton toolStripButtonForward;
+ private System.Windows.Forms.ToolStripButton toolStripButtonRewind;
+ private System.Windows.Forms.ToolStripButton toolStripButtonLoop;
+ private System.Windows.Forms.Button buttonA;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
+ private System.Windows.Forms.ToolStripLabel toolStripLabelPosition;
+ private System.Windows.Forms.Button buttonB;
+ private System.Windows.Forms.Button buttonC;
+ private System.Windows.Forms.Button buttonD;
+ private System.Windows.Forms.ToolTip toolTip1;
+ private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
+ private System.Windows.Forms.ToolStripMenuItem clearToolStripMenuItem;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.ToolStripLabel toolStripLabel1;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
+ private System.Windows.Forms.MenuStrip menuStrip1;
+ private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem openSavedComparisonToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem saveComparisonToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem contentsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem compareModeToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem currentPositionToolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem skipBackToolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem restartToolStripMenuItem1;
+ private System.Windows.Forms.ToolStripLabel toolStripLabel2;
+ private System.Windows.Forms.ToolStripLabel toolStripLabelLength;
+ private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem selectFileToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem propertiesToolStripMenuItem;
+ private System.Windows.Forms.ToolStripButton toolStripButtonShuffle;
+ private System.Windows.Forms.ToolStripSeparator toolStripSeparator3;
+
+ }
+}
+
diff --git a/MixDiff/MixDiffForm.cs b/MixDiff/MixDiffForm.cs
new file mode 100644
index 00000000..b4847bf7
--- /dev/null
+++ b/MixDiff/MixDiffForm.cs
@@ -0,0 +1,612 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using System.Xml;
+using NAudio.Wave;
+using MarkHeath.AudioUtils.Properties;
+
+namespace MarkHeath.AudioUtils
+{
+ public partial class MixDiffForm : Form
+ {
+ private PlaybackStatus playbackStatus;
+ private IWavePlayer wavePlayer;
+ readonly Font BigFont = new Font("Verdana", 36, FontStyle.Bold);
+ readonly Font EmptyFont = new Font("Verdana", 16, FontStyle.Bold);
+ private WaveMixerStream32 mixer;
+ private int skipSeconds;
+ private Button selectedButton;
+ private CompareMode compareMode;
+ private List fileButtons;
+ private bool shuffled;
+
+ public MixDiffForm()
+ {
+ InitializeComponent();
+ mixer = new WaveMixerStream32();
+ mixer.AutoStop = false;
+ skipSeconds = 3;
+ fileButtons = new List();
+ fileButtons.Add(buttonA);
+ fileButtons.Add(buttonB);
+ fileButtons.Add(buttonC);
+ fileButtons.Add(buttonD);
+
+ }
+
+
+ private bool LoadFile(Button button)
+ {
+ // prompt for file load
+ OpenFileDialog openFileDialog = new OpenFileDialog();
+ openFileDialog.Filter = "WAV Files (*.wav)|*.wav";
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ MixdownInfo info = new MixdownInfo(openFileDialog.FileName);
+ // TODO: sort this out - post shuffle
+ info.Letter = button.Name.Substring(button.Name.Length - 1);
+ SetButtonInfo(button, info);
+ return true;
+ }
+ return false;
+ }
+
+ private void SetButtonInfo(Button button, MixdownInfo info)
+ {
+ if (button.Tag != null)
+ {
+ ClearFile(button);
+ }
+ button.Tag = info;
+ SetButtonAppearance(button);
+ mixer.AddInputStream(info.Stream);
+ SetLengthLabel();
+ }
+
+ private void ClearFile(Button button)
+ {
+ if (button.Tag != null)
+ {
+ MixdownInfo buttonInfo = button.Tag as MixdownInfo;
+ mixer.RemoveInputStream(buttonInfo.Stream);
+ buttonInfo.Stream.Close();
+ button.Tag = null;
+ SetButtonAppearance(button);
+ }
+ }
+
+ private void SetButtonAppearance(Button button)
+ {
+ MixdownInfo info = button.Tag as MixdownInfo;
+ if (info == null)
+ {
+ button.Text = "";
+ button.Font = EmptyFont;
+ toolTip1.SetToolTip(button, null);
+ }
+ else
+ {
+ if (shuffled)
+ {
+ button.Text = "?";
+ toolTip1.SetToolTip(button, null);
+ button.Font = BigFont;
+ }
+ else
+ {
+ button.Text = info.Letter;
+ toolTip1.SetToolTip(button, info.FileName);
+ button.Font = BigFont;
+ }
+ }
+ }
+
+ private void OnMixButtonClick(object sender, EventArgs e)
+ {
+ Button button = sender as Button;
+ if (button.Tag == null)
+ {
+ if (LoadFile(button))
+ {
+ SelectButton(button);
+ }
+ }
+ else
+ {
+ SelectButton(button);
+ }
+ }
+
+ private void SelectButton(Button button)
+ {
+ MixdownInfo info;
+ if (selectedButton != null)
+ {
+ selectedButton.BackColor = SystemColors.Control;
+ selectedButton.ForeColor = SystemColors.ControlText;
+ info = selectedButton.Tag as MixdownInfo;
+ if (info != null)
+ {
+ // can be null if active button is cleared
+ info.Stream.Mute = true;
+ }
+ }
+ info = button.Tag as MixdownInfo;
+ button.ForeColor = Color.DarkGreen;
+ button.BackColor = Color.LightGoldenrodYellow;
+ info.Stream.Mute = false;
+ selectedButton = button;
+ if (playbackStatus == PlaybackStatus.Playing)
+ {
+ if (compareMode == CompareMode.SkipBack)
+ {
+ SkipBack();
+ }
+ else if (compareMode == CompareMode.Restart)
+ {
+ Rewind();
+ }
+ }
+ }
+
+ private void Play()
+ {
+ if (playbackStatus != PlaybackStatus.Playing)
+ {
+ if (playbackStatus != PlaybackStatus.Paused)
+ {
+ Rewind();
+ }
+ if (wavePlayer == null)
+ {
+ wavePlayer = new WaveOut();
+ wavePlayer.Init(mixer);
+ }
+ wavePlayer.Play();
+ playbackStatus = PlaybackStatus.Playing;
+ timer1.Start();
+ }
+ }
+
+ private void Stop()
+ {
+ if (playbackStatus != PlaybackStatus.Stopped)
+ {
+ if (wavePlayer != null)
+ {
+ wavePlayer.Stop();
+ playbackStatus = PlaybackStatus.Stopped;
+ timer1.Stop();
+ }
+ Rewind();
+ }
+ }
+
+ private void Pause()
+ {
+ if (playbackStatus == PlaybackStatus.Playing)
+ {
+ wavePlayer.Pause();
+ playbackStatus = PlaybackStatus.Paused;
+ }
+ }
+
+ private void toolStripButtonPlay_Click(object sender, EventArgs e)
+ {
+ Play();
+ }
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ if (playbackStatus == PlaybackStatus.Playing)
+ {
+ if (mixer.Position > mixer.Length)
+ {
+ if (toolStripButtonLoop.Checked)
+ {
+ Rewind();
+ }
+ else
+ {
+ Stop();
+ }
+ }
+
+ SetPositionLabel();
+ }
+ }
+
+ private void SetPositionLabel()
+ {
+ TimeSpan currentTime = mixer.CurrentTime;
+ toolStripLabelPosition.Text = String.Format("{0:00}:{1:00}:{2:00}.{3:000}",
+ currentTime.Hours,
+ currentTime.Minutes,
+ currentTime.Seconds,
+ currentTime.Milliseconds);
+ }
+
+ private void SetLengthLabel()
+ {
+ TimeSpan length = mixer.TotalTime;
+ toolStripLabelLength.Text = String.Format("{0:00}:{1:00}:{2:00}",
+ length.Hours,
+ length.Minutes,
+ length.Seconds);
+
+ }
+
+ private void MixDiffForm_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (playbackStatus != PlaybackStatus.Stopped)
+ {
+ Stop();
+ }
+ if (wavePlayer != null)
+ {
+ wavePlayer.Dispose();
+ wavePlayer = null;
+ }
+ if (mixer != null)
+ {
+ mixer.Dispose();
+ mixer = null;
+ }
+ }
+
+ private void toolStripButtonStop_Click(object sender, EventArgs e)
+ {
+ Stop();
+ }
+
+ private void toolStripButtonPause_Click(object sender, EventArgs e)
+ {
+ Pause();
+ }
+
+ private void toolStripButtonBack_Click(object sender, EventArgs e)
+ {
+ SkipBack();
+ }
+
+ private void toolStripButtonForward_Click(object sender, EventArgs e)
+ {
+ if (mixer != null)
+ {
+ mixer.CurrentTime += TimeSpan.FromSeconds(skipSeconds);
+ SetPositionLabel();
+
+ }
+ }
+
+ private void toolStripButtonRewind_Click(object sender, EventArgs e)
+ {
+ Rewind();
+ }
+
+ private void SkipBack()
+ {
+ if (mixer != null)
+ {
+ mixer.CurrentTime += TimeSpan.FromSeconds(0 - skipSeconds);
+ SetPositionLabel();
+ }
+ }
+
+ private void Rewind()
+ {
+ if (mixer != null)
+ {
+ mixer.Position = 0;
+ SetPositionLabel();
+ }
+ }
+
+ public CompareMode CompareMode
+ {
+ get { return compareMode; }
+ set
+ {
+ compareMode = value;
+ currentPositionToolStripMenuItem1.Checked = (compareMode == CompareMode.CurrentPosition);
+ skipBackToolStripMenuItem1.Checked = (compareMode == CompareMode.SkipBack);
+ restartToolStripMenuItem1.Checked = (compareMode == CompareMode.Restart);
+ }
+
+ }
+
+ private void currentPositionToolStripMenuItem1_Click(object sender, EventArgs e)
+ {
+ CompareMode = CompareMode.CurrentPosition;
+ }
+
+ private void skipBackToolStripMenuItem1_Click(object sender, EventArgs e)
+ {
+ CompareMode = CompareMode.SkipBack;
+ }
+
+ private void restartToolStripMenuItem1_Click(object sender, EventArgs e)
+ {
+ CompareMode = CompareMode.Restart;
+ }
+
+ private void exitToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ this.Close();
+ }
+
+ private void clearToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ Button button = (Button)contextMenuStrip1.SourceControl;
+ ClearFile(button);
+ }
+
+ private void selectFileToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ Button button = (Button)contextMenuStrip1.SourceControl;
+ LoadFile(button);
+ }
+
+ private void propertiesToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ Button button = (Button)contextMenuStrip1.SourceControl;
+ if (button.Tag != null)
+ {
+ MixdownInfo mixdownInfo = button.Tag as MixdownInfo;
+ PropertiesForm propertiesForm = new PropertiesForm(mixdownInfo);
+ if (propertiesForm.ShowDialog() == DialogResult.OK)
+ {
+
+ }
+ }
+ }
+
+ private void saveComparisonToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ SaveFileDialog saveFileDialog = new SaveFileDialog();
+ saveFileDialog.DefaultExt = ".MixDiff";
+ saveFileDialog.Filter = "*.MixDiff (MixDiff Comparison Files)|*.MixDiff|*.* (All Files)|*.*";
+ if (saveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ SaveComparison(saveFileDialog.FileName);
+ }
+ }
+
+ private void SaveComparison(string fileName)
+ {
+ XmlWriterSettings settings = new XmlWriterSettings();
+ settings.Indent = true;
+ settings.NewLineOnAttributes = true;
+
+ using (XmlWriter writer = XmlWriter.Create(fileName,settings))
+ {
+ writer.WriteStartElement("MixDiff");
+ writer.WriteStartElement("Settings");
+ writer.WriteAttributeString("CompareMode", compareMode.ToString());
+ writer.WriteEndElement();
+ foreach (Button button in fileButtons)
+ {
+ WriteMixdownInfo(writer, button.Tag as MixdownInfo);
+ }
+ writer.WriteEndElement();
+ }
+ }
+
+ private void LoadComparison(string fileName)
+ {
+ XmlDocument document = new XmlDocument();
+ document.Load(fileName);
+ XmlNode compareModeNode = document.SelectSingleNode("MixDiff/Settings/@CompareMode");
+ CompareMode = (CompareMode)Enum.Parse(typeof(CompareMode), compareModeNode.Value);
+ XmlNodeList mixes = document.SelectNodes("MixDiff/Mix");
+ int buttonIndex = 0;
+ foreach(XmlNode mixNode in mixes)
+ {
+ Button button = fileButtons[buttonIndex++];
+ MixdownInfo info = new MixdownInfo(mixNode.SelectSingleNode("@FileName").Value);
+ info.DelayMilliseconds = Int32.Parse(mixNode.SelectSingleNode("@DelayMilliseconds").Value);
+ info.OffsetMilliseconds = Int32.Parse(mixNode.SelectSingleNode("@OffsetMilliseconds").Value);
+ info.VolumeDecibels = Int32.Parse(mixNode.SelectSingleNode("@VolumeDecibels").Value);
+ info.Letter = button.Name.Substring(button.Name.Length - 1);
+ info.Stream.Mute = true;
+ SetButtonInfo(button,info);
+ }
+ SelectButton(fileButtons[0]);
+ }
+
+ private void WriteMixdownInfo(XmlWriter writer, MixdownInfo mixdownInfo)
+ {
+ if (mixdownInfo != null)
+ {
+ writer.WriteStartElement("Mix");
+ writer.WriteAttributeString("FileName", mixdownInfo.FileName);
+ writer.WriteAttributeString("DelayMilliseconds", mixdownInfo.DelayMilliseconds.ToString());
+ writer.WriteAttributeString("OffsetMilliseconds", mixdownInfo.OffsetMilliseconds.ToString());
+ writer.WriteAttributeString("VolumeDecibels", mixdownInfo.VolumeDecibels.ToString());
+ writer.WriteEndElement();
+ }
+ }
+
+ private void openSavedComparisonToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ OpenFileDialog openFileDialog = new OpenFileDialog();
+ openFileDialog.DefaultExt = ".MixDiff";
+ openFileDialog.Filter = "*.MixDiff (MixDiff Comparison Files)|*.MixDiff|*.* (All Files)|*.*";
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ Stop();
+ foreach(Button button in fileButtons)
+ {
+ ClearFile(button);
+ }
+ LoadComparison(openFileDialog.FileName);
+ }
+ }
+
+ private void contentsToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ System.Diagnostics.Process.Start("http://www.codeplex.com/naudio/Wiki/View.aspx?title=MixDiff");
+ }
+ catch (Exception)
+ {
+ MessageBox.Show("Failed to launch browser to show help file");
+ }
+
+ }
+
+ private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ NAudio.Utils.AboutForm aboutForm = new NAudio.Utils.AboutForm();
+ aboutForm.ShowDialog();
+ }
+
+ private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ SettingsForm settingsForm = new SettingsForm();
+ if (settingsForm.ShowDialog() == DialogResult.OK)
+ {
+ // TODO: reopen WaveOut device
+ }
+
+ }
+
+ private void toolStripButtonShuffle_Click(object sender, EventArgs e)
+ {
+ if (!shuffled)
+ {
+ Shuffle();
+ }
+ else
+ {
+ Reveal();
+ }
+
+ toolStripButtonShuffle.Checked = shuffled;
+ }
+
+ private void Shuffle()
+ {
+ List mixdowns = new List();
+ foreach (Button button in fileButtons)
+ {
+ if (button.Tag != null)
+ {
+ mixdowns.Add(button.Tag as MixdownInfo);
+ }
+ }
+
+
+ Random rand = new Random();
+ if (mixdowns.Count < 2)
+ {
+ MessageBox.Show("You need to have at least two files to compare to use the shuffle feature",
+ Application.ProductName);
+ return;
+ }
+
+ shuffled = true;
+
+ if (Settings.Default.UseAllSlots)
+ {
+ foreach (Button button in fileButtons)
+ {
+ if (button.Tag == null)
+ {
+ button.Tag = mixdowns[rand.Next() % mixdowns.Count];
+ }
+ }
+ }
+
+ for (int n = 0; n < 12; n++)
+ {
+ int swap1 = rand.Next() % fileButtons.Count;
+ int swap2 = rand.Next() % fileButtons.Count;
+ if (swap1 != swap2)
+ {
+ object tag1 = fileButtons[swap1].Tag;
+ fileButtons[swap1].Tag = fileButtons[swap2].Tag;
+ fileButtons[swap2].Tag = tag1;
+ }
+ }
+
+ Button firstMix = null;
+ foreach (Button button in fileButtons)
+ {
+ SetButtonAppearance(button);
+ if (button.Tag != null && firstMix == null)
+ firstMix = button;
+ }
+ SelectButton(firstMix);
+ }
+
+ private void Reveal()
+ {
+ shuffled = false;
+ foreach (Button button in fileButtons)
+ {
+ SetButtonAppearance(button);
+ }
+ }
+
+ private void MixDiffForm_KeyDown(object sender, KeyEventArgs e)
+ {
+ switch (e.KeyCode)
+ {
+ case Keys.D1:
+ OnMixButtonClick(buttonA, EventArgs.Empty);
+ e.Handled = true;
+ break;
+ case Keys.D2:
+ OnMixButtonClick(buttonB, EventArgs.Empty);
+ e.Handled = true;
+ break;
+ case Keys.D3:
+ OnMixButtonClick(buttonC, EventArgs.Empty);
+ e.Handled = true;
+ break;
+ case Keys.D4:
+ OnMixButtonClick(buttonD, EventArgs.Empty);
+ e.Handled = true;
+ break;
+ case Keys.Space:
+ if (playbackStatus != PlaybackStatus.Playing)
+ {
+ Play();
+ }
+ else
+ {
+ Pause();
+ }
+ e.Handled = true;
+ break;
+ case Keys.Home:
+ Rewind();
+ e.Handled = true;
+ break;
+ }
+
+ }
+
+
+ }
+
+ public enum CompareMode
+ {
+ CurrentPosition,
+ SkipBack,
+ Restart,
+ }
+
+ enum PlaybackStatus
+ {
+ Stopped,
+ Playing,
+ Paused
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/MixDiffForm.resx b/MixDiff/MixDiffForm.resx
new file mode 100644
index 00000000..eef0b526
--- /dev/null
+++ b/MixDiff/MixDiffForm.resx
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 208, 17
+
+
+ 116, 17
+
+
+ 353, 17
+
+
+ 436, 17
+
+
\ No newline at end of file
diff --git a/MixDiff/MixDiffStream.cs b/MixDiff/MixDiffStream.cs
new file mode 100644
index 00000000..ad4169f0
--- /dev/null
+++ b/MixDiff/MixDiffStream.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+
+namespace MarkHeath.AudioUtils
+{
+ public class MixDiffStream : WaveStream
+ {
+ WaveOffsetStream offsetStream;
+ WaveChannel32 channelSteam;
+ bool muted;
+ float volume;
+
+ public MixDiffStream(string fileName)
+ {
+ WaveFileReader reader = new WaveFileReader(fileName);
+ offsetStream = new WaveOffsetStream(reader);
+ channelSteam = new WaveChannel32(offsetStream);
+ muted = false;
+ volume = 1.0f;
+ }
+
+ public override int BlockAlign
+ {
+ get
+ {
+ return channelSteam.BlockAlign;
+ }
+ }
+
+ public override WaveFormat WaveFormat
+ {
+ get { return channelSteam.WaveFormat; }
+ }
+
+ public override long Length
+ {
+ get { return channelSteam.Length; }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ return channelSteam.Position;
+ }
+ set
+ {
+ channelSteam.Position = value;
+ }
+ }
+
+ public bool Mute
+ {
+ get
+ {
+ return muted;
+ }
+ set
+ {
+ muted = value;
+ if (muted)
+ {
+ channelSteam.Volume = 0.0f;
+ }
+ else
+ {
+ // reset the volume
+ Volume = Volume;
+ }
+ }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return channelSteam.Read(buffer, offset, count);
+ }
+
+ public override bool HasData(int count)
+ {
+ return channelSteam.HasData(count);
+ }
+
+ public float Volume
+ {
+ get
+ {
+ return volume;
+ }
+ set
+ {
+ volume = value;
+ if (!Mute)
+ {
+ channelSteam.Volume = volume;
+ }
+ }
+ }
+
+ public TimeSpan PreDelay
+ {
+ get { return offsetStream.StartTime; }
+ set { offsetStream.StartTime = value; }
+ }
+
+ public TimeSpan Offset
+ {
+ get { return offsetStream.SourceOffset; }
+ set { offsetStream.SourceOffset = value; }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (channelSteam != null)
+ {
+ channelSteam.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/MixDiff/MixdownInfo.cs b/MixDiff/MixdownInfo.cs
new file mode 100644
index 00000000..325a9f41
--- /dev/null
+++ b/MixDiff/MixdownInfo.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+using NAudio.Utils;
+
+namespace MarkHeath.AudioUtils
+{
+ public class MixdownInfo
+ {
+ string fileName;
+ string letter;
+ MixDiffStream stream;
+ int offsetMilliseconds;
+ int delayMilliseconds;
+ int volumeDecibels;
+
+ public MixdownInfo(string fileName)
+ {
+ this.fileName = fileName;
+ this.stream = new MixDiffStream(fileName);
+ }
+
+ public string FileName
+ {
+ get { return fileName; }
+ }
+
+ public string Letter
+ {
+ get { return letter; }
+ set { letter = value; }
+ }
+
+ public MixDiffStream Stream
+ {
+ get { return stream; }
+ }
+
+ public int OffsetMilliseconds
+ {
+ get { return offsetMilliseconds; }
+ set
+ {
+ offsetMilliseconds = value;
+ stream.Offset = TimeSpan.FromMilliseconds(offsetMilliseconds);
+ }
+ }
+
+ public int DelayMilliseconds
+ {
+ get { return delayMilliseconds; }
+ set
+ {
+ delayMilliseconds = value;
+ stream.PreDelay = TimeSpan.FromMilliseconds(delayMilliseconds);
+ }
+ }
+
+ public int VolumeDecibels
+ {
+ get
+ {
+ return volumeDecibels;
+ }
+ set
+ {
+ volumeDecibels = value;
+ stream.Volume = (float) Decibels.DecibelsToLinear(volumeDecibels);
+ }
+ }
+ }
+}
diff --git a/MixDiff/Program.cs b/MixDiff/Program.cs
new file mode 100644
index 00000000..abf756cf
--- /dev/null
+++ b/MixDiff/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace MarkHeath.AudioUtils
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new MixDiffForm());
+ }
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/Properties/AssemblyInfo.cs b/MixDiff/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..fd101804
--- /dev/null
+++ b/MixDiff/Properties/AssemblyInfo.cs
@@ -0,0 +1,79 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MixDiff")]
+[assembly: AssemblyDescription("Mix Comparison Utility")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Mark Heath")]
+[assembly: AssemblyProduct("MixDiff")]
+[assembly: AssemblyCopyright("Copyright © Mark Heath 2006")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("12d6167b-bcab-4b93-b7ae-460fee425dfa")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("0.1.7.0")]
+[assembly: AssemblyFileVersion("0.1.7.0")]
+
+// build 1 19 Oct 2006
+// initial project created
+// build 2 8 Mar 2007
+// project added to codeplex
+// basic ability to load and play a file
+// can switch between files
+// build 3 16 Mar 2007
+// pausing ability
+// fixed a bug with stop
+// compare mode added
+// initial slot clearing code
+// build 4 19 Mar 2007
+// more context menu actions
+// properties form for offset and volume setting
+// build 5 20 Mar 2007
+// can save settings to a MixDiff file
+// offset is now working
+// build 6 22 Mar 2007
+// can load from a MixDiff file
+// help and about dialogs
+// build 7 23 Mar 2007
+// comparison file opening bug fixes & enhancements
+// beginnings of a shuffle feature
+
+// TODO list - key features for version 1
+// 24 bit support
+// select WaveOut output device
+// configurable skip back amount
+// Error handling
+// Implement shuffle & reveal feature
+// Keyboard support
+// Options form to allow selection of WaveOut device
+// update WaveOut to allow Init to be called multiple times
+// fix the negative time thing
+// repositioning drag bar
+// status bar
+
+
+//Extra features:
+//MixDiff file should offer relative path support
+//master volume
+//32 bit file support
+//mp3 support
+//less important: 8 bit, 64 bit file support
+//mismatched sample rate support
diff --git a/MixDiff/Properties/Resources.Designer.cs b/MixDiff/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..05e1f95c
--- /dev/null
+++ b/MixDiff/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.17929
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace MarkHeath.AudioUtils.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MarkHeath.AudioUtils.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/MixDiff/Properties/Resources.resx b/MixDiff/Properties/Resources.resx
new file mode 100644
index 00000000..ffecec85
--- /dev/null
+++ b/MixDiff/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/MixDiff/Properties/Settings.Designer.cs b/MixDiff/Properties/Settings.Designer.cs
new file mode 100644
index 00000000..04b13b08
--- /dev/null
+++ b/MixDiff/Properties/Settings.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.17929
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace MarkHeath.AudioUtils.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("-1")]
+ public int WaveOutDevice {
+ get {
+ return ((int)(this["WaveOutDevice"]));
+ }
+ set {
+ this["WaveOutDevice"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("True")]
+ public bool UseAllSlots {
+ get {
+ return ((bool)(this["UseAllSlots"]));
+ }
+ set {
+ this["UseAllSlots"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("5")]
+ public int SkipBackSeconds {
+ get {
+ return ((int)(this["SkipBackSeconds"]));
+ }
+ set {
+ this["SkipBackSeconds"] = value;
+ }
+ }
+ }
+}
diff --git a/MixDiff/Properties/Settings.settings b/MixDiff/Properties/Settings.settings
new file mode 100644
index 00000000..c9e57f5a
--- /dev/null
+++ b/MixDiff/Properties/Settings.settings
@@ -0,0 +1,15 @@
+
+
+
+
+
+ -1
+
+
+ True
+
+
+ 5
+
+
+
\ No newline at end of file
diff --git a/MixDiff/PropertiesForm.Designer.cs b/MixDiff/PropertiesForm.Designer.cs
new file mode 100644
index 00000000..78df58f5
--- /dev/null
+++ b/MixDiff/PropertiesForm.Designer.cs
@@ -0,0 +1,168 @@
+namespace MarkHeath.AudioUtils
+{
+ partial class PropertiesForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.label1 = new System.Windows.Forms.Label();
+ this.textBoxDelay = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.trackBarVolume = new System.Windows.Forms.TrackBar();
+ this.textBoxVolume = new System.Windows.Forms.TextBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.textBoxOffset = new System.Windows.Forms.TextBox();
+ this.buttonOK = new System.Windows.Forms.Button();
+ this.buttonCancel = new System.Windows.Forms.Button();
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).BeginInit();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(12, 13);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(148, 13);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "Add Silence to Beginning (ms)";
+ //
+ // textBoxDelay
+ //
+ this.textBoxDelay.Location = new System.Drawing.Point(180, 10);
+ this.textBoxDelay.MaxLength = 5;
+ this.textBoxDelay.Name = "textBoxDelay";
+ this.textBoxDelay.Size = new System.Drawing.Size(100, 20);
+ this.textBoxDelay.TabIndex = 1;
+ this.textBoxDelay.Text = "0";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(12, 65);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(76, 13);
+ this.label2.TabIndex = 2;
+ this.label2.Text = "Adjust volume:";
+ //
+ // trackBarVolume
+ //
+ this.trackBarVolume.Location = new System.Drawing.Point(12, 88);
+ this.trackBarVolume.Maximum = 18;
+ this.trackBarVolume.Minimum = -48;
+ this.trackBarVolume.Name = "trackBarVolume";
+ this.trackBarVolume.Size = new System.Drawing.Size(268, 45);
+ this.trackBarVolume.TabIndex = 3;
+ this.trackBarVolume.TickFrequency = 6;
+ this.trackBarVolume.Scroll += new System.EventHandler(this.trackBarVolume_Scroll);
+ //
+ // textBoxVolume
+ //
+ this.textBoxVolume.Location = new System.Drawing.Point(180, 62);
+ this.textBoxVolume.Name = "textBoxVolume";
+ this.textBoxVolume.ReadOnly = true;
+ this.textBoxVolume.Size = new System.Drawing.Size(100, 20);
+ this.textBoxVolume.TabIndex = 4;
+ this.textBoxVolume.Text = "0 dB";
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(12, 39);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(117, 13);
+ this.label3.TabIndex = 0;
+ this.label3.Text = "Skip Start of Audio (ms)";
+ //
+ // textBoxOffset
+ //
+ this.textBoxOffset.Location = new System.Drawing.Point(180, 36);
+ this.textBoxOffset.MaxLength = 5;
+ this.textBoxOffset.Name = "textBoxOffset";
+ this.textBoxOffset.Size = new System.Drawing.Size(100, 20);
+ this.textBoxOffset.TabIndex = 1;
+ this.textBoxOffset.Text = "0";
+ //
+ // buttonOK
+ //
+ this.buttonOK.Location = new System.Drawing.Point(124, 140);
+ this.buttonOK.Name = "buttonOK";
+ this.buttonOK.Size = new System.Drawing.Size(75, 23);
+ this.buttonOK.TabIndex = 5;
+ this.buttonOK.Text = "OK";
+ this.buttonOK.UseVisualStyleBackColor = true;
+ this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
+ //
+ // buttonCancel
+ //
+ this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.buttonCancel.Location = new System.Drawing.Point(205, 139);
+ this.buttonCancel.Name = "buttonCancel";
+ this.buttonCancel.Size = new System.Drawing.Size(75, 23);
+ this.buttonCancel.TabIndex = 6;
+ this.buttonCancel.Text = "Cancel";
+ this.buttonCancel.UseVisualStyleBackColor = true;
+ //
+ // PropertiesForm
+ //
+ this.AcceptButton = this.buttonOK;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.buttonCancel;
+ this.ClientSize = new System.Drawing.Size(288, 175);
+ this.Controls.Add(this.buttonCancel);
+ this.Controls.Add(this.buttonOK);
+ this.Controls.Add(this.textBoxVolume);
+ this.Controls.Add(this.trackBarVolume);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.textBoxOffset);
+ this.Controls.Add(this.textBoxDelay);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.label1);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "PropertiesForm";
+ this.ShowInTaskbar = false;
+ this.Text = "Properties";
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarVolume)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TextBox textBoxDelay;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.TrackBar trackBarVolume;
+ private System.Windows.Forms.TextBox textBoxVolume;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.TextBox textBoxOffset;
+ private System.Windows.Forms.Button buttonOK;
+ private System.Windows.Forms.Button buttonCancel;
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/PropertiesForm.cs b/MixDiff/PropertiesForm.cs
new file mode 100644
index 00000000..0a4949de
--- /dev/null
+++ b/MixDiff/PropertiesForm.cs
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+
+namespace MarkHeath.AudioUtils
+{
+ public partial class PropertiesForm : Form
+ {
+ MixdownInfo mixdownInfo;
+
+ public PropertiesForm(MixdownInfo mixdownInfo)
+ {
+ InitializeComponent();
+ this.mixdownInfo = mixdownInfo;
+ textBoxDelay.Text = mixdownInfo.DelayMilliseconds.ToString();
+ textBoxOffset.Text = mixdownInfo.OffsetMilliseconds.ToString();
+ trackBarVolume.Value = mixdownInfo.VolumeDecibels;
+ trackBarVolume_Scroll(this, EventArgs.Empty);
+ }
+
+ private void trackBarVolume_Scroll(object sender, EventArgs e)
+ {
+ textBoxVolume.Text = String.Format("{0} dB", trackBarVolume.Value);
+ mixdownInfo.VolumeDecibels = trackBarVolume.Value;
+ }
+
+ private void buttonOK_Click(object sender, EventArgs e)
+ {
+ int delay = 0;
+ int offset = 0;
+ bool parse = Int32.TryParse(textBoxDelay.Text,out delay);
+ if(!parse || delay < 0)
+ {
+ MessageBox.Show("Please enter a valid number of milliseconds for the delay.");
+ textBoxDelay.Focus();
+ return;
+ }
+ parse = Int32.TryParse(textBoxOffset.Text, out offset);
+ if (!parse || offset < 0)
+ {
+ MessageBox.Show("Please enter a valid number of milliseconds to trim from the start.");
+ textBoxOffset.Focus();
+ return;
+ }
+ mixdownInfo.DelayMilliseconds = delay;
+ mixdownInfo.OffsetMilliseconds = offset;
+ mixdownInfo.VolumeDecibels = trackBarVolume.Value;
+ this.Close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/PropertiesForm.resx b/MixDiff/PropertiesForm.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/MixDiff/PropertiesForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/MixDiff/Resources/Back.png b/MixDiff/Resources/Back.png
new file mode 100644
index 00000000..357e645c
Binary files /dev/null and b/MixDiff/Resources/Back.png differ
diff --git a/MixDiff/Resources/Forward.png b/MixDiff/Resources/Forward.png
new file mode 100644
index 00000000..43e95404
Binary files /dev/null and b/MixDiff/Resources/Forward.png differ
diff --git a/MixDiff/Resources/Loop.png b/MixDiff/Resources/Loop.png
new file mode 100644
index 00000000..be08c0ec
Binary files /dev/null and b/MixDiff/Resources/Loop.png differ
diff --git a/MixDiff/Resources/Pause.png b/MixDiff/Resources/Pause.png
new file mode 100644
index 00000000..189a5453
Binary files /dev/null and b/MixDiff/Resources/Pause.png differ
diff --git a/MixDiff/Resources/Play.png b/MixDiff/Resources/Play.png
new file mode 100644
index 00000000..93bc2af1
Binary files /dev/null and b/MixDiff/Resources/Play.png differ
diff --git a/MixDiff/Resources/Rewind.png b/MixDiff/Resources/Rewind.png
new file mode 100644
index 00000000..c8feb415
Binary files /dev/null and b/MixDiff/Resources/Rewind.png differ
diff --git a/MixDiff/Resources/Shuffle.png b/MixDiff/Resources/Shuffle.png
new file mode 100644
index 00000000..243ed82e
Binary files /dev/null and b/MixDiff/Resources/Shuffle.png differ
diff --git a/MixDiff/Resources/Stop.png b/MixDiff/Resources/Stop.png
new file mode 100644
index 00000000..49038201
Binary files /dev/null and b/MixDiff/Resources/Stop.png differ
diff --git a/MixDiff/SettingsForm.Designer.cs b/MixDiff/SettingsForm.Designer.cs
new file mode 100644
index 00000000..a7d82c14
--- /dev/null
+++ b/MixDiff/SettingsForm.Designer.cs
@@ -0,0 +1,143 @@
+namespace MarkHeath.AudioUtils
+{
+ partial class SettingsForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.label1 = new System.Windows.Forms.Label();
+ this.comboBoxOutputDevice = new System.Windows.Forms.ComboBox();
+ this.buttonOK = new System.Windows.Forms.Button();
+ this.buttonCancel = new System.Windows.Forms.Button();
+ this.label2 = new System.Windows.Forms.Label();
+ this.textBoxSkipBackSeconds = new System.Windows.Forms.TextBox();
+ this.checkBoxUseAllSlots = new System.Windows.Forms.CheckBox();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(13, 13);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(76, 13);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "Output Device";
+ //
+ // comboBoxOutputDevice
+ //
+ this.comboBoxOutputDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxOutputDevice.FormattingEnabled = true;
+ this.comboBoxOutputDevice.Location = new System.Drawing.Point(121, 10);
+ this.comboBoxOutputDevice.Name = "comboBoxOutputDevice";
+ this.comboBoxOutputDevice.Size = new System.Drawing.Size(265, 21);
+ this.comboBoxOutputDevice.TabIndex = 1;
+ //
+ // buttonOK
+ //
+ this.buttonOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonOK.Location = new System.Drawing.Point(225, 97);
+ this.buttonOK.Name = "buttonOK";
+ this.buttonOK.Size = new System.Drawing.Size(75, 23);
+ this.buttonOK.TabIndex = 2;
+ this.buttonOK.Text = "OK";
+ this.buttonOK.UseVisualStyleBackColor = true;
+ this.buttonOK.Click += new System.EventHandler(this.buttonOK_Click);
+ //
+ // buttonCancel
+ //
+ this.buttonCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+ this.buttonCancel.Location = new System.Drawing.Point(311, 97);
+ this.buttonCancel.Name = "buttonCancel";
+ this.buttonCancel.Size = new System.Drawing.Size(75, 23);
+ this.buttonCancel.TabIndex = 3;
+ this.buttonCancel.Text = "Cancel";
+ this.buttonCancel.UseVisualStyleBackColor = true;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(12, 40);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(142, 13);
+ this.label2.TabIndex = 4;
+ this.label2.Text = "Skip Back Time (in seconds)";
+ //
+ // textBoxSkipBackSeconds
+ //
+ this.textBoxSkipBackSeconds.Location = new System.Drawing.Point(286, 37);
+ this.textBoxSkipBackSeconds.MaxLength = 3;
+ this.textBoxSkipBackSeconds.Name = "textBoxSkipBackSeconds";
+ this.textBoxSkipBackSeconds.Size = new System.Drawing.Size(100, 20);
+ this.textBoxSkipBackSeconds.TabIndex = 5;
+ //
+ // checkBoxUseAllSlots
+ //
+ this.checkBoxUseAllSlots.AutoSize = true;
+ this.checkBoxUseAllSlots.Location = new System.Drawing.Point(16, 65);
+ this.checkBoxUseAllSlots.Name = "checkBoxUseAllSlots";
+ this.checkBoxUseAllSlots.Size = new System.Drawing.Size(176, 17);
+ this.checkBoxUseAllSlots.TabIndex = 6;
+ this.checkBoxUseAllSlots.Text = "Use all available slots for shuffle";
+ this.checkBoxUseAllSlots.UseVisualStyleBackColor = true;
+ //
+ // SettingsForm
+ //
+ this.AcceptButton = this.buttonOK;
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.CancelButton = this.buttonCancel;
+ this.ClientSize = new System.Drawing.Size(398, 132);
+ this.Controls.Add(this.checkBoxUseAllSlots);
+ this.Controls.Add(this.textBoxSkipBackSeconds);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.buttonCancel);
+ this.Controls.Add(this.buttonOK);
+ this.Controls.Add(this.comboBoxOutputDevice);
+ this.Controls.Add(this.label1);
+ this.MaximizeBox = false;
+ this.MinimizeBox = false;
+ this.Name = "SettingsForm";
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
+ this.Text = "Settings";
+ this.Load += new System.EventHandler(this.SettingsForm_Load);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.ComboBox comboBoxOutputDevice;
+ private System.Windows.Forms.Button buttonOK;
+ private System.Windows.Forms.Button buttonCancel;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.TextBox textBoxSkipBackSeconds;
+ private System.Windows.Forms.CheckBox checkBoxUseAllSlots;
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/SettingsForm.cs b/MixDiff/SettingsForm.cs
new file mode 100644
index 00000000..08552a9c
--- /dev/null
+++ b/MixDiff/SettingsForm.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+using MarkHeath.AudioUtils.Properties;
+
+namespace MarkHeath.AudioUtils
+{
+ public partial class SettingsForm : Form
+ {
+ public SettingsForm()
+ {
+ InitializeComponent();
+ }
+
+ private void SettingsForm_Load(object sender, EventArgs e)
+ {
+ comboBoxOutputDevice.ValueMember = "DeviceNumber";
+ comboBoxOutputDevice.DisplayMember = "DeviceName";
+ comboBoxOutputDevice.Items.Add(new WaveOutComboItem("(Default)",-1));
+ for (int n = 0; n < WaveOut.DeviceCount; n++)
+ {
+ WaveOutCapabilities waveOutCaps = WaveOut.GetCapabilities(n);
+ comboBoxOutputDevice.Items.Add(new WaveOutComboItem(waveOutCaps.ProductName, n));
+ }
+ Settings settings = Settings.Default;
+ textBoxSkipBackSeconds.Text = settings.SkipBackSeconds.ToString();
+ checkBoxUseAllSlots.Checked = settings.UseAllSlots;
+ comboBoxOutputDevice.SelectedValue = settings.WaveOutDevice;
+ }
+
+ private void buttonOK_Click(object sender, EventArgs e)
+ {
+ Settings settings = Settings.Default;
+
+ int skipBackSeconds = 5;
+ bool parsed = Int32.TryParse(textBoxSkipBackSeconds.Text, out skipBackSeconds);
+ if (!parsed || skipBackSeconds <= 0)
+ {
+ MessageBox.Show("Please enter a valid number of skip back seconds");
+ textBoxSkipBackSeconds.Focus();
+ return;
+ }
+
+ this.Close();
+ }
+ }
+
+ class WaveOutComboItem
+ {
+ string deviceName;
+ int deviceNumber;
+
+ public WaveOutComboItem(string deviceName, int deviceNumber)
+ {
+ this.deviceName = deviceName;
+ this.deviceNumber = deviceNumber;
+ }
+
+ public int DeviceNumber
+ {
+ get { return deviceNumber; }
+ }
+
+ public string DeviceName
+ {
+ get { return deviceName; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/MixDiff/SettingsForm.resx b/MixDiff/SettingsForm.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/MixDiff/SettingsForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/MixDiff/app.config b/MixDiff/app.config
new file mode 100644
index 00000000..7118d69f
--- /dev/null
+++ b/MixDiff/app.config
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+ -1
+
+
+ True
+
+
+ 5
+
+
+
+
diff --git a/NAudio.Win8/NAudio.Win8.csproj b/NAudio.Win8/NAudio.Win8.csproj
new file mode 100644
index 00000000..6fd3cd5d
--- /dev/null
+++ b/NAudio.Win8/NAudio.Win8.csproj
@@ -0,0 +1,801 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}
+ Library
+ Properties
+ NAudio.Win8
+ NAudio.Win8
+ en-US
+ 512
+ {BC8A1FFA-BEE3-4634-8014-F334798102B3};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE;NETFX_CORE
+ prompt
+ 4
+ false
+ bin\Debug\NAudio.Win8.XML
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE;NETFX_CORE
+ prompt
+ 4
+ false
+ bin\Release\NAudio.Win8.XML
+
+
+ true
+ bin\ARM\Debug\
+ DEBUG;TRACE;NETFX_CORE
+ ;2008
+ full
+ ARM
+ false
+ prompt
+ true
+
+
+ bin\ARM\Release\
+ TRACE;NETFX_CORE
+ true
+ ;2008
+ pdbonly
+ ARM
+ false
+ prompt
+ true
+
+
+ true
+ bin\x64\Debug\
+ DEBUG;TRACE;NETFX_CORE
+ ;2008
+ full
+ x64
+ false
+ prompt
+ true
+
+
+ bin\x64\Release\
+ TRACE;NETFX_CORE
+ true
+ ;2008
+ pdbonly
+ x64
+ false
+ prompt
+ true
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE;NETFX_CORE
+ ;2008
+ full
+ x86
+ false
+ prompt
+ true
+
+
+ bin\x86\Release\
+ TRACE;NETFX_CORE
+ true
+ ;2008
+ pdbonly
+ x86
+ false
+ prompt
+ true
+
+
+
+
+
+
+ Codecs\ALawDecoder.cs
+
+
+ Codecs\ALawEncoder.cs
+
+
+ Codecs\G722Codec.cs
+
+
+ Codecs\MuLawDecoder.cs
+
+
+ Codecs\MuLawEncoder.cs
+
+
+ CoreAudioApi\AudioCaptureClient.cs
+
+
+ CoreAudioApi\AudioClient.cs
+
+
+ CoreAudioApi\AudioClientBufferFlags.cs
+
+
+ CoreAudioApi\AudioClientProperties.cs
+
+
+ CoreAudioApi\AudioClientShareMode.cs
+
+
+ CoreAudioApi\AudioClientStreamFlags.cs
+
+
+ CoreAudioApi\AudioClientStreamOptions.cs
+
+
+ CoreAudioApi\AudioClockClient.cs
+
+
+ CoreAudioApi\AudioEndpointVolume.cs
+
+
+ CoreAudioApi\AudioEndpointVolumeCallback.cs
+
+
+ CoreAudioApi\AudioEndpointVolumeChannel.cs
+
+
+ CoreAudioApi\AudioEndpointVolumeChannels.cs
+
+
+ CoreAudioApi\AudioEndpointVolumeNotificationDelegate.cs
+
+
+ CoreAudioApi\AudioEndpointVolumeStepInformation.cs
+
+
+ CoreAudioApi\AudioEndpointVolumeVolumeRange.cs
+
+
+ CoreAudioApi\AudioMeterInformation.cs
+
+
+ CoreAudioApi\AudioMeterInformationChannels.cs
+
+
+ CoreAudioApi\AudioRenderClient.cs
+
+
+ CoreAudioApi\AudioSessionControl.cs
+
+
+ CoreAudioApi\AudioSessionEventsCallback.cs
+
+
+ CoreAudioApi\AudioSessionManager.cs
+
+
+ CoreAudioApi\AudioStreamCategory.cs
+
+
+ CoreAudioApi\AudioVolumeNotificationData.cs
+
+
+ CoreAudioApi\DataFlow.cs
+
+
+ CoreAudioApi\DeviceState.cs
+
+
+ CoreAudioApi\EEndpointHardwareSupport.cs
+
+
+ CoreAudioApi\Interfaces\AudioVolumeNotificationDataStruct.cs
+
+
+ CoreAudioApi\Interfaces\Blob.cs
+
+
+ CoreAudioApi\Interfaces\ClsCtx.cs
+
+
+ CoreAudioApi\Interfaces\ErrorCodes.cs
+
+
+ CoreAudioApi\Interfaces\IAudioCaptureClient.cs
+
+
+ CoreAudioApi\Interfaces\IAudioClient.cs
+
+
+ CoreAudioApi\Interfaces\IAudioClock2.cs
+
+
+ CoreAudioApi\Interfaces\IAudioEndpointVolume.cs
+
+
+ CoreAudioApi\Interfaces\IAudioEndpointVolumeCallback.cs
+
+
+ CoreAudioApi\Interfaces\IAudioMeterInformation.cs
+
+
+ CoreAudioApi\Interfaces\IAudioRenderClient.cs
+
+
+ CoreAudioApi\Interfaces\IAudioSessionControl.cs
+
+
+ CoreAudioApi\Interfaces\IAudioSessionEvents.cs
+
+
+ CoreAudioApi\Interfaces\IAudioSessionEventsHandler.cs
+
+
+ CoreAudioApi\Interfaces\IAudioSessionManager.cs
+
+
+ CoreAudioApi\Interfaces\IMMDevice.cs
+
+
+ CoreAudioApi\Interfaces\IMMDeviceCollection.cs
+
+
+ CoreAudioApi\Interfaces\IMMDeviceEnumerator.cs
+
+
+ CoreAudioApi\Interfaces\IMMEndpoint.cs
+
+
+ CoreAudioApi\Interfaces\IMMNotificationClient.cs
+
+
+ CoreAudioApi\Interfaces\IPropertyStore.cs
+
+
+ CoreAudioApi\Interfaces\ISimpleAudioVolume.cs
+
+
+ CoreAudioApi\Interfaces\MMDeviceEnumeratorComObject.cs
+
+
+ CoreAudioApi\Interfaces\StorageAccessMode.cs
+
+
+ CoreAudioApi\MMDevice.cs
+
+
+ CoreAudioApi\MMDeviceCollection.cs
+
+
+ CoreAudioApi\MMDeviceEnumerator.cs
+
+
+ CoreAudioApi\PropertyKey.cs
+
+
+ CoreAudioApi\PropertyKeys.cs
+
+
+ CoreAudioApi\PropertyStore.cs
+
+
+ CoreAudioApi\PropertyStoreProperty.cs
+
+
+ CoreAudioApi\PropVariant.cs
+
+
+ CoreAudioApi\Role.cs
+
+
+ CoreAudioApi\SimpleAudioVolume.cs
+
+
+ Dmo\AudioMediaSubtypes.cs
+
+
+ Dmo\IWMResamplerProps.cs
+
+
+ Dsp\BiQuadFilter.cs
+
+
+ Dsp\Complex.cs
+
+
+ Dsp\EnvelopeDetector.cs
+
+
+ Dsp\EnvelopeGenerator.cs
+
+
+ Dsp\FastFourierTransform.cs
+
+
+ Dsp\ImpulseResponseConvolution.cs
+
+
+ Dsp\SimpleCompressor.cs
+
+
+ Dsp\SimpleGate.cs
+
+
+ Dsp\WdlResampler.cs
+
+
+ FileFormats\Mp3\ChannelMode.cs
+
+
+ FileFormats\Mp3\Id3v2Tag.cs
+
+
+ FileFormats\Mp3\IMp3FrameDecompressor.cs
+
+
+ FileFormats\Mp3\Mp3Frame.cs
+
+
+ FileFormats\Mp3\MpegLayer.cs
+
+
+ FileFormats\Mp3\MpegVersion.cs
+
+
+ FileFormats\Mp3\XingHeader.cs
+
+
+ FileFormats\SoundFont\Generator.cs
+
+
+ FileFormats\SoundFont\GeneratorBuilder.cs
+
+
+ FileFormats\SoundFont\GeneratorEnum.cs
+
+
+ FileFormats\SoundFont\InfoChunk.cs
+
+
+ FileFormats\SoundFont\Instrument.cs
+
+
+ FileFormats\SoundFont\InstrumentBuilder.cs
+
+
+ FileFormats\SoundFont\Modulator.cs
+
+
+ FileFormats\SoundFont\ModulatorBuilder.cs
+
+
+ FileFormats\SoundFont\ModulatorType.cs
+
+
+ FileFormats\SoundFont\Preset.cs
+
+
+ FileFormats\SoundFont\PresetBuilder.cs
+
+
+ FileFormats\SoundFont\PresetsChunk.cs
+
+
+ FileFormats\SoundFont\RiffChunk.cs
+
+
+ FileFormats\SoundFont\SampleDataChunk.cs
+
+
+ FileFormats\SoundFont\SampleHeader.cs
+
+
+ FileFormats\SoundFont\SampleHeaderBuilder.cs
+
+
+ FileFormats\SoundFont\SampleMode.cs
+
+
+ FileFormats\SoundFont\SFSampleLink.cs
+
+
+ FileFormats\SoundFont\SFVersion.cs
+
+
+ FileFormats\SoundFont\SFVersionBuilder.cs
+
+
+ FileFormats\SoundFont\SoundFont.cs
+
+
+ FileFormats\SoundFont\StructureBuilder.cs
+
+
+ FileFormats\SoundFont\Zone.cs
+
+
+ FileFormats\SoundFont\ZoneBuilder.cs
+
+
+ MediaFoundation\AudioSubtypes.cs
+
+
+ MediaFoundation\IMFActivate.cs
+
+
+ MediaFoundation\IMFAttributes.cs
+
+
+ MediaFoundation\IMFByteStream.cs
+
+
+ MediaFoundation\IMFCollection.cs
+
+
+ MediaFoundation\IMFMediaBuffer.cs
+
+
+ MediaFoundation\IMFMediaEvent.cs
+
+
+ MediaFoundation\IMFMediaType.cs
+
+
+ MediaFoundation\IMFReadWriteClassFactory.cs
+
+
+ MediaFoundation\IMFSample.cs
+
+
+ MediaFoundation\IMFSinkWriter.cs
+
+
+ MediaFoundation\IMFSourceReader.cs
+
+
+ MediaFoundation\IMFTransform.cs
+
+
+ MediaFoundation\MediaEventType.cs
+
+
+ MediaFoundation\MediaFoundationAttributes.cs
+
+
+ MediaFoundation\MediaFoundationErrors.cs
+
+
+ MediaFoundation\MediaFoundationHelpers.cs
+
+
+ MediaFoundation\MediaFoundationInterop.cs
+
+
+ MediaFoundation\MediaFoundationTransform.cs
+
+
+ MediaFoundation\MediaFoundationTransformCategories.cs
+
+
+ MediaFoundation\MediaType.cs
+
+
+ MediaFoundation\MediaTypes.cs
+
+
+ MediaFoundation\MFT_INPUT_STREAM_INFO.cs
+
+
+ MediaFoundation\MFT_MESSAGE_TYPE.cs
+
+
+ MediaFoundation\MFT_OUTPUT_DATA_BUFFER.cs
+
+
+ MediaFoundation\MFT_OUTPUT_STREAM_INFO.cs
+
+
+ MediaFoundation\MFT_REGISTER_TYPE_INFO.cs
+
+
+ MediaFoundation\MF_SINK_WRITER_STATISTICS.cs
+
+
+ MediaFoundation\_MFT_ENUM_FLAG.cs
+
+
+ MediaFoundation\_MFT_INPUT_STATUS_FLAGS.cs
+
+
+ MediaFoundation\_MFT_INPUT_STREAM_INFO_FLAGS.cs
+
+
+ MediaFoundation\_MFT_OUTPUT_DATA_BUFFER_FLAGS.cs
+
+
+ MediaFoundation\_MFT_OUTPUT_STATUS_FLAGS.cs
+
+
+ MediaFoundation\_MFT_OUTPUT_STREAM_INFO_FLAGS.cs
+
+
+ MediaFoundation\_MFT_PROCESS_OUTPUT_FLAGS.cs
+
+
+ MediaFoundation\_MFT_PROCESS_OUTPUT_STATUS.cs
+
+
+ MediaFoundation\_MFT_SET_TYPE_FLAGS.cs
+
+
+ Utils\BufferHelpers.cs
+
+
+ Utils\ByteArrayExtensions.cs
+
+
+ Utils\ByteEncoding.cs
+
+
+ Utils\CircularBuffer.cs
+
+
+ Utils\Decibels.cs
+
+
+ Utils\FieldDescriptionAttribute.cs
+
+
+ Utils\FieldDescriptionHelper.cs
+
+
+ Utils\HResult.cs
+
+
+ Utils\IEEE.cs
+
+
+ Utils\IgnoreDisposeStream.cs
+
+
+ Utils\MergeSort.cs
+
+
+ Utils\NativeMethods.cs
+
+
+ Wave\WaveInputs\WaveInEventArgs.cs
+
+
+ Wave\SampleChunkConverters\ISampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\Mono16SampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\Mono24SampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\Mono8SampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\MonoFloatSampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\Stereo16SampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\Stereo24SampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\Stereo8SampleChunkConverter.cs
+
+
+ Wave\SampleChunkConverters\StereoFloatSampleChunkConverter.cs
+
+
+ Wave\SampleProviders\FadeInOutSampleProvider.cs
+
+
+ Wave\SampleProviders\MeteringSampleProvider.cs
+
+
+ Wave\SampleProviders\MixingSampleProvider.cs
+
+
+ Wave\SampleProviders\MonoToStereoSampleProvider.cs
+
+
+ Wave\SampleProviders\MultiplexingSampleProvider.cs
+
+
+ Wave\SampleProviders\NotifyingSampleProvider.cs
+
+
+ Wave\SampleProviders\OffsetSampleProvider.cs
+
+
+ Wave\SampleProviders\PanningSampleProvider.cs
+
+
+ Wave\SampleProviders\Pcm16BitToSampleProvider.cs
+
+
+ Wave\SampleProviders\Pcm24BitToSampleProvider.cs
+
+
+ Wave\SampleProviders\Pcm32BitToSampleProvider.cs
+
+
+ Wave\SampleProviders\Pcm8BitToSampleProvider.cs
+
+
+ Wave\SampleProviders\SampleChannel.cs
+
+
+ Wave\SampleProviders\SampleProviderConverterBase.cs
+
+
+ Wave\SampleProviders\SampleProviderConverters.cs
+
+
+ Wave\SampleProviders\SampleToWaveProvider.cs
+
+
+ Wave\SampleProviders\SampleToWaveProvider16.cs
+
+
+ Wave\SampleProviders\SampleToWaveProvider24.cs
+
+
+ Wave\SampleProviders\SignalGenerator.cs
+
+
+ Wave\SampleProviders\VolumeSampleProvider.cs
+
+
+ Wave\SampleProviders\WaveToSampleProvider.cs
+
+
+ Wave\SampleProviders\WaveToSampleProvider64.cs
+
+
+ Wave\SampleProviders\WdlResamplingSampleProvider.cs
+
+
+ Wave\WaveFormats\AdpcmWaveFormat.cs
+
+
+ Wave\WaveFormats\Gsm610WaveFormat.cs
+
+
+ Wave\WaveFormats\ImaAdpcmWaveFormat.cs
+
+
+ Wave\WaveFormats\Mp3WaveFormat.cs
+
+
+ Wave\WaveFormats\OggWaveFormat.cs
+
+
+ Wave\WaveFormats\TrueSpeechWaveFormat.cs
+
+
+ Wave\WaveFormats\WaveFormat.cs
+
+
+ Wave\WaveFormats\WaveFormatEncoding.cs
+
+
+ Wave\WaveFormats\WaveFormatExtensible.cs
+
+
+ Wave\WaveFormats\WaveFormatExtraData.cs
+
+
+ Wave\WaveFormats\WmaWaveFormat.cs
+
+
+ Wave\WaveInputs\IWaveIn.cs
+
+
+ Wave\WaveOutputs\IWaveBuffer.cs
+
+
+ Wave\WaveOutputs\IWaveProvider.cs
+
+
+ Wave\WaveOutputs\IWaveProviderFloat.cs
+
+
+ Wave\WaveOutputs\PlaybackState.cs
+
+
+ Wave\WaveOutputs\StoppedEventArgs.cs
+
+
+ Wave\WaveOutputs\WaveBuffer.cs
+
+
+ Wave\WaveProviders\BufferedWaveProvider.cs
+
+
+ Wave\WaveProviders\MonoToStereoProvider16.cs
+
+
+ Wave\WaveProviders\MultiplexingWaveProvider.cs
+
+
+ Wave\WaveProviders\StereoToMonoProvider16.cs
+
+
+ Wave\WaveProviders\VolumeWaveProvider16.cs
+
+
+ Wave\WaveProviders\Wave16toFloatProvider.cs
+
+
+ Wave\WaveProviders\WaveFloatTo16Provider.cs
+
+
+ Wave\WaveProviders\WaveInProvider.cs
+
+
+ Wave\WaveProviders\WaveProvider16.cs
+
+
+ Wave\WaveProviders\WaveProvider32.cs
+
+
+ Wave\WaveStreams\BlockAlignReductionStream.cs
+
+
+ Wave\WaveStreams\ISampleNotifier.cs
+
+
+ Wave\WaveStreams\MediaFoundationReader.cs
+
+
+ Wave\WaveStreams\RawSourceWaveStream.cs
+
+
+ Wave\WaveStreams\SimpleCompressorStream.cs
+
+
+ Wave\WaveStreams\WaveChannel32.cs
+
+
+ Wave\WaveStreams\WaveOffsetStream.cs
+
+
+ Wave\WaveStreams\WaveStream.cs
+
+
+
+
+
+
+
+
+ 11.0
+
+
+
+
\ No newline at end of file
diff --git a/NAudio.Win8/Properties/AssemblyInfo.cs b/NAudio.Win8/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..22b7f499
--- /dev/null
+++ b/NAudio.Win8/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NAudio.Win8")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NAudio.Win8")]
+[assembly: AssemblyCopyright("Copyright © Mark Heath 2013")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.7.0.15")]
+[assembly: AssemblyFileVersion("1.7.0.15")]
+[assembly: ComVisible(false)]
\ No newline at end of file
diff --git a/NAudio.Win8/Wave/WaveExtensionMethods.cs b/NAudio.Win8/Wave/WaveExtensionMethods.cs
new file mode 100644
index 00000000..f190e7db
--- /dev/null
+++ b/NAudio.Win8/Wave/WaveExtensionMethods.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Linq;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Useful extension methods to make switching between WaveAndSampleProvider easier
+ ///
+ public static class WaveExtensionMethods
+ {
+ ///
+ /// Converts a WaveProvider into a SampleProvider (only works for PCM)
+ ///
+ /// WaveProvider to convert
+ ///
+ public static ISampleProvider ToSampleProvider(this IWaveProvider waveProvider)
+ {
+ return SampleProviderConverters.ConvertWaveProviderIntoSampleProvider(waveProvider);
+ }
+
+ ///
+ /// Allows sending a SampleProvider directly to an IWavePlayer without needing to convert
+ /// back to an IWaveProvider
+ ///
+ /// The WavePlayer
+ ///
+ ///
+ public static void Init(this IWavePlayer wavePlayer, ISampleProvider sampleProvider, bool convertTo16Bit = false)
+ {
+ IWaveProvider provider = convertTo16Bit ? (IWaveProvider)new SampleToWaveProvider16(sampleProvider) : new SampleToWaveProvider(sampleProvider);
+ wavePlayer.Init(() => provider);
+ }
+ }
+}
diff --git a/NAudio.Win8/Wave/WaveInputs/WasapiCaptureRT.cs b/NAudio.Win8/Wave/WaveInputs/WasapiCaptureRT.cs
new file mode 100644
index 00000000..96414c44
--- /dev/null
+++ b/NAudio.Win8/Wave/WaveInputs/WasapiCaptureRT.cs
@@ -0,0 +1,346 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using NAudio.CoreAudioApi;
+using NAudio.CoreAudioApi.Interfaces;
+using NAudio.Wave;
+using System.Threading;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using NAudio.Win8.Wave.WaveOutputs;
+using Windows.Devices.Enumeration;
+using Windows.Media.Devices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Audio Capture using Wasapi
+ /// See http://msdn.microsoft.com/en-us/library/dd370800%28VS.85%29.aspx
+ ///
+ public class WasapiCaptureRT : IWaveIn
+ {
+ private const long REFTIMES_PER_SEC = 10000000;
+ private const long REFTIMES_PER_MILLISEC = 10000;
+ private volatile bool stop;
+ private byte[] recordBuffer;
+ private readonly string device;
+ private int bytesPerFrame;
+ private WaveFormat waveFormat;
+
+ ///
+ /// Indicates recorded data is available
+ ///
+ public event EventHandler DataAvailable;
+
+ ///
+ /// Indicates that all recorded data has now been received.
+ ///
+ public event EventHandler RecordingStopped;
+ private int latencyMilliseconds;
+
+ ///
+ /// Initialises a new instance of the WASAPI capture class
+ ///
+ public WasapiCaptureRT() :
+ this(GetDefaultCaptureDevice())
+ {
+ }
+
+ ///
+ /// Initialises a new instance of the WASAPI capture class
+ ///
+ /// Capture device to use
+ public WasapiCaptureRT(string device)
+ {
+ this.device = device;
+ //this.waveFormat = audioClient.MixFormat;
+ }
+
+ ///
+ /// Recording wave format
+ ///
+ public virtual WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ set { waveFormat = value; }
+ }
+
+ ///
+ /// Way of enumerating all the audio capture devices available on the system
+ ///
+ ///
+ public async static Task> GetCaptureDevices()
+ {
+ var audioCaptureSelector = MediaDevice.GetAudioCaptureSelector();
+
+ // (a PropertyKey)
+ var supportsEventDrivenMode = "{1da5d803-d492-4edd-8c23-e0c0ffee7f0e} 7";
+
+ var captureDevices = await DeviceInformation.FindAllAsync(audioCaptureSelector, new[] { supportsEventDrivenMode } );
+ return captureDevices;
+ }
+
+ ///
+ /// Gets the default audio capture device
+ ///
+ /// The default audio capture device
+ public static string GetDefaultCaptureDevice()
+ {
+ var defaultCaptureDeviceId = MediaDevice.GetDefaultAudioCaptureId(AudioDeviceRole.Default);
+ return defaultCaptureDeviceId;
+ }
+
+ private async Task Activate()
+ {
+ var icbh = new ActivateAudioInterfaceCompletionHandler(
+ ac2 =>
+ {
+ InitializeCaptureDevice((IAudioClient)ac2);
+ /*var wfx = new WaveFormat(44100, 16, 2);
+ int hr = ac2.Initialize(AudioClientShareMode.Shared,
+ AudioClientStreamFlags.None,
+ //AudioClientStreamFlags.EventCallback | AudioClientStreamFlags.NoPersist,
+ 10000000, 0, wfx, IntPtr.Zero);
+ Marshal.ThrowExceptionForHR(hr);*/
+ });
+ var IID_IAudioClient2 = new Guid("726778CD-F60A-4eda-82DE-E47610CD78AA");
+ IActivateAudioInterfaceAsyncOperation activationOperation;
+ NativeMethods.ActivateAudioInterfaceAsync(device, IID_IAudioClient2, IntPtr.Zero, icbh, out activationOperation);
+ var audioClient2 = await icbh;
+ return new AudioClient((IAudioClient)audioClient2);
+ }
+
+ private void InitializeCaptureDevice(IAudioClient audioClientInterface)
+ {
+ var audioClient = new AudioClient((IAudioClient)audioClientInterface);
+ if (waveFormat == null)
+ {
+ this.waveFormat = audioClient.MixFormat;
+ }
+
+ long requestedDuration = REFTIMES_PER_MILLISEC * 100;
+
+
+ if (!audioClient.IsFormatSupported(AudioClientShareMode.Shared, WaveFormat))
+ {
+ throw new ArgumentException("Unsupported Wave Format");
+ }
+
+ var streamFlags = GetAudioClientStreamFlags();
+
+ audioClient.Initialize(AudioClientShareMode.Shared,
+ streamFlags,
+ requestedDuration,
+ 0,
+ this.waveFormat,
+ Guid.Empty);
+
+
+ int bufferFrameCount = audioClient.BufferSize;
+ this.bytesPerFrame = this.waveFormat.Channels * this.waveFormat.BitsPerSample / 8;
+ this.recordBuffer = new byte[bufferFrameCount * bytesPerFrame];
+ Debug.WriteLine(string.Format("record buffer size = {0}", this.recordBuffer.Length));
+
+ // Get back the effective latency from AudioClient
+ latencyMilliseconds = (int)(audioClient.StreamLatency / 10000);
+ }
+
+ ///
+ /// To allow overrides to specify different flags (e.g. loopback)
+ ///
+ protected virtual AudioClientStreamFlags GetAudioClientStreamFlags()
+ {
+ return AudioClientStreamFlags.EventCallback;
+ }
+
+ ///
+ /// Start Recording
+ ///
+ public async void StartRecording()
+ {
+ this.stop = false;
+ var audioClient = await Activate();
+ Task.Run(() => CaptureThread(audioClient));
+
+ /*Task.Run(
+ async () =>
+ {
+ var audioClient = await Activate();
+ //InitializeCaptureDevice(audioClient); - now done in the activate callback
+ CaptureThread(audioClient);
+ });*/
+
+ Debug.WriteLine("Thread starting...");
+
+ }
+
+ ///
+ /// Stop Recording
+ ///
+ public void StopRecording()
+ {
+ this.stop = true;
+ // todo: wait for thread to end
+ // todo: could signal the event
+ }
+
+ private void CaptureThread(AudioClient client)
+ {
+ Exception exception = null;
+ try
+ {
+ DoRecording(client);
+ }
+ catch (Exception e)
+ {
+ exception = e;
+ }
+
+ }
+
+ private void DoRecording(AudioClient client)
+ {
+ Debug.WriteLine(client.BufferSize);
+
+ var buf = new Byte[client.BufferSize * bytesPerFrame];
+
+ int bufLength = 0;
+ int minPacketSize = waveFormat.AverageBytesPerSecond / 100; //100ms
+
+ IntPtr hEvent = NativeMethods.CreateEventEx(IntPtr.Zero, IntPtr.Zero, 0, EventAccess.EVENT_ALL_ACCESS);
+ client.SetEventHandle(hEvent);
+
+ try
+ {
+ AudioCaptureClient capture = client.AudioCaptureClient;
+ client.Start();
+
+ int packetSize = capture.GetNextPacketSize();
+
+ while (!this.stop)
+ {
+ IntPtr pData = IntPtr.Zero;
+ int numFramesToRead = 0;
+ AudioClientBufferFlags dwFlags = 0;
+
+ if (packetSize == 0)
+ {
+ if (NativeMethods.WaitForSingleObjectEx(hEvent, 100, true) != 0)
+ {
+ throw new Exception("Capture event timeout");
+ }
+ }
+
+ pData = capture.GetBuffer(out numFramesToRead, out dwFlags);
+
+ if ((int)(dwFlags & AudioClientBufferFlags.Silent) > 0)
+ {
+ pData = IntPtr.Zero;
+ }
+
+ if (numFramesToRead == 0) { continue; }
+
+ int capturedBytes = numFramesToRead * bytesPerFrame;
+
+ if (pData == IntPtr.Zero)
+ {
+ Array.Clear(buf, bufLength, capturedBytes);
+ }
+ else
+ {
+ Marshal.Copy(pData, buf, bufLength, capturedBytes);
+ }
+
+ bufLength += capturedBytes;
+
+ capture.ReleaseBuffer(numFramesToRead);
+
+ if (bufLength >= minPacketSize)
+ {
+ if (DataAvailable != null)
+ {
+ DataAvailable(this, new WaveInEventArgs(buf, bufLength));
+ }
+ bufLength = 0;
+ }
+
+ packetSize = capture.GetNextPacketSize();
+ }
+ }
+ catch (Exception ex)
+ {
+ RaiseRecordingStopped(ex);
+ Debug.WriteLine("stop wasapi");
+ }
+ finally
+ {
+ RaiseRecordingStopped(null);
+
+ NativeMethods.CloseHandle(hEvent);
+ client.Stop();
+ client.Dispose();
+ }
+ }
+
+ private void RaiseRecordingStopped(Exception exception)
+ {
+ var handler = RecordingStopped;
+ if (handler != null)
+ {
+ handler(this, new StoppedEventArgs(exception));
+ }
+ }
+
+ private void ReadNextPacket(AudioCaptureClient capture)
+ {
+ IntPtr buffer;
+ int framesAvailable;
+ AudioClientBufferFlags flags;
+ int packetSize = capture.GetNextPacketSize();
+ int recordBufferOffset = 0;
+ //Debug.WriteLine(string.Format("packet size: {0} samples", packetSize / 4));
+
+ while (packetSize != 0)
+ {
+ buffer = capture.GetBuffer(out framesAvailable, out flags);
+
+ int bytesAvailable = framesAvailable * bytesPerFrame;
+
+ // apparently it is sometimes possible to read more frames than we were expecting?
+ // fix suggested by Michael Feld:
+ int spaceRemaining = Math.Max(0, recordBuffer.Length - recordBufferOffset);
+ if (spaceRemaining < bytesAvailable && recordBufferOffset > 0)
+ {
+ if (DataAvailable != null) DataAvailable(this, new WaveInEventArgs(recordBuffer, recordBufferOffset));
+ recordBufferOffset = 0;
+ }
+
+ // if not silence...
+ if ((flags & AudioClientBufferFlags.Silent) != AudioClientBufferFlags.Silent)
+ {
+ Marshal.Copy(buffer, recordBuffer, recordBufferOffset, bytesAvailable);
+ }
+ else
+ {
+ Array.Clear(recordBuffer, recordBufferOffset, bytesAvailable);
+ }
+ recordBufferOffset += bytesAvailable;
+ capture.ReleaseBuffer(framesAvailable);
+ packetSize = capture.GetNextPacketSize();
+ }
+ if (DataAvailable != null)
+ {
+ DataAvailable(this, new WaveInEventArgs(recordBuffer, recordBufferOffset));
+ }
+ }
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ StopRecording();
+ }
+ }
+}
diff --git a/NAudio.Win8/Wave/WaveOutputs/IWavePlayer.cs b/NAudio.Win8/Wave/WaveOutputs/IWavePlayer.cs
new file mode 100644
index 00000000..6605a3d9
--- /dev/null
+++ b/NAudio.Win8/Wave/WaveOutputs/IWavePlayer.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Represents the interface to a device that can play audio
+ ///
+ public interface IWavePlayer : IDisposable
+ {
+ ///
+ /// Begin playback
+ ///
+ void Play();
+
+ ///
+ /// Stop playback
+ ///
+ void Stop();
+
+ ///
+ /// Pause Playback
+ ///
+ void Pause();
+
+ ///
+ /// Obsolete init method
+ ///
+ ///
+ ///
+ [Obsolete]
+ Task Init(IWaveProvider waveProvider);
+
+ ///
+ /// Initialise playback
+ ///
+ /// Function to create the waveprovider to be played
+ /// Called on the playback thread
+ void Init(Func waveProviderFunc);
+
+ ///
+ /// Current playback state
+ ///
+ PlaybackState PlaybackState { get; }
+
+ ///
+ /// Indicates that playback has gone into a stopped state due to
+ /// reaching the end of the input stream or an error has been encountered during playback
+ ///
+ event EventHandler PlaybackStopped;
+ }
+}
diff --git a/NAudio.Win8/Wave/WaveOutputs/WasapiOutRT.cs b/NAudio.Win8/Wave/WaveOutputs/WasapiOutRT.cs
new file mode 100644
index 00000000..1ecb7738
--- /dev/null
+++ b/NAudio.Win8/Wave/WaveOutputs/WasapiOutRT.cs
@@ -0,0 +1,647 @@
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Windows.System.Threading;
+using NAudio.CoreAudioApi;
+using NAudio.CoreAudioApi.Interfaces;
+using NAudio.Dsp;
+using NAudio.Wave;
+using Windows.Media.Devices;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudio.Win8.Wave.WaveOutputs
+{
+ enum WasapiOutState
+ {
+ Uninitialized,
+ Stopped,
+ Paused,
+ Playing,
+ Stopping,
+ Disposing,
+ Disposed
+ }
+
+ ///
+ /// WASAPI Out for Windows RT
+ ///
+ public class WasapiOutRT : IWavePlayer
+ {
+ private AudioClient audioClient;
+ private readonly string device;
+ private readonly AudioClientShareMode shareMode;
+ private AudioRenderClient renderClient;
+ private int latencyMilliseconds;
+ private int bufferFrameCount;
+ private int bytesPerFrame;
+ private byte[] readBuffer;
+ private volatile WasapiOutState playbackState;
+ private WaveFormat outputFormat;
+ private bool resamplerNeeded;
+ private IntPtr frameEventWaitHandle;
+ private readonly SynchronizationContext syncContext;
+ private bool isInitialized;
+ private readonly AutoResetEvent playThreadEvent;
+
+ ///
+ /// Playback Stopped
+ ///
+ public event EventHandler PlaybackStopped;
+
+ ///
+ /// WASAPI Out using default audio endpoint
+ ///
+ /// ShareMode - shared or exclusive
+ /// Desired latency in milliseconds
+ public WasapiOutRT(AudioClientShareMode shareMode, int latency) :
+ this(GetDefaultAudioEndpoint(), shareMode, latency)
+ {
+
+ }
+
+ ///
+ /// Creates a new WASAPI Output
+ ///
+ /// Device to use
+ ///
+ ///
+ public WasapiOutRT(string device, AudioClientShareMode shareMode, int latency)
+ {
+ this.device = device;
+ this.shareMode = shareMode;
+ this.latencyMilliseconds = latency;
+ this.syncContext = SynchronizationContext.Current;
+ playThreadEvent = new AutoResetEvent(false);
+ }
+
+ ///
+ /// Properties of the client's audio stream.
+ /// Set before calling init
+ ///
+ private AudioClientProperties? audioClientProperties = null;
+
+ private Func waveProviderFunc;
+
+ ///
+ /// Sets the parameters that describe the properties of the client's audio stream.
+ ///
+ /// Boolean value to indicate whether or not the audio stream is hardware-offloaded.
+ /// An enumeration that is used to specify the category of the audio stream.
+ /// A bit-field describing the characteristics of the stream. Supported in Windows 8.1 and later.
+ public void SetClientProperties(bool useHardwareOffload, AudioStreamCategory category, AudioClientStreamOptions options)
+ {
+ audioClientProperties = new AudioClientProperties()
+ {
+ cbSize = (uint) Marshal.SizeOf(typeof (AudioClientProperties)),
+ bIsOffload = Convert.ToInt32(useHardwareOffload),
+ eCategory = category,
+ Options = options
+ };
+ }
+
+ private async Task Activate()
+ {
+ var icbh = new ActivateAudioInterfaceCompletionHandler(
+ ac2 =>
+ {
+ if (this.audioClientProperties != null)
+ {
+ IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(this.audioClientProperties.Value));
+ Marshal.StructureToPtr(this.audioClientProperties.Value, p, false);
+ ac2.SetClientProperties(p);
+ // TODO: consider whether we can marshal this without the need for AllocHGlobal
+ }
+
+ /*var wfx = new WaveFormat(44100, 16, 2);
+ int hr = ac2.Initialize(AudioClientShareMode.Shared,
+ AudioClientStreamFlags.EventCallback | AudioClientStreamFlags.NoPersist,
+ 10000000, 0, wfx, IntPtr.Zero);*/
+ });
+ var IID_IAudioClient2 = new Guid("726778CD-F60A-4eda-82DE-E47610CD78AA");
+ IActivateAudioInterfaceAsyncOperation activationOperation;
+ NativeMethods.ActivateAudioInterfaceAsync(device, IID_IAudioClient2, IntPtr.Zero, icbh, out activationOperation);
+ var audioClient2 = await icbh;
+ this.audioClient = new AudioClient((IAudioClient)audioClient2);
+ }
+
+ private static string GetDefaultAudioEndpoint()
+ {
+ // can't use the MMDeviceEnumerator in WinRT
+
+ return MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default);
+ }
+
+ private async void PlayThread()
+ {
+ await Activate();
+ var playbackProvider = Init();
+ bool isClientRunning = false;
+ try
+ {
+ if (this.resamplerNeeded)
+ {
+ var resampler = new WdlResamplingSampleProvider(playbackProvider.ToSampleProvider(), outputFormat.SampleRate);
+ playbackProvider = new SampleToWaveProvider(resampler);
+ }
+
+ // fill a whole buffer
+ bufferFrameCount = audioClient.BufferSize;
+ bytesPerFrame = outputFormat.Channels*outputFormat.BitsPerSample/8;
+ readBuffer = new byte[bufferFrameCount*bytesPerFrame];
+ FillBuffer(playbackProvider, bufferFrameCount);
+ int timeout = 3 * latencyMilliseconds;
+
+ while (playbackState != WasapiOutState.Disposed)
+ {
+ if (playbackState != WasapiOutState.Playing)
+ {
+ playThreadEvent.WaitOne(500);
+ }
+
+ // If still playing and notification is ok
+ if (playbackState == WasapiOutState.Playing)
+ {
+ if (!isClientRunning)
+ {
+ audioClient.Start();
+ isClientRunning = true;
+ }
+ // If using Event Sync, Wait for notification from AudioClient or Sleep half latency
+ var r = NativeMethods.WaitForSingleObjectEx(frameEventWaitHandle, timeout, true);
+ if (r != 0) throw new InvalidOperationException("Timed out waiting for event");
+ // See how much buffer space is available.
+ int numFramesPadding = 0;
+ // In exclusive mode, always ask the max = bufferFrameCount = audioClient.BufferSize
+ numFramesPadding = (shareMode == AudioClientShareMode.Shared) ? audioClient.CurrentPadding : 0;
+
+ int numFramesAvailable = bufferFrameCount - numFramesPadding;
+ if (numFramesAvailable > 0)
+ {
+ FillBuffer(playbackProvider, numFramesAvailable);
+ }
+ }
+
+ if (playbackState == WasapiOutState.Stopping)
+ {
+ // play the buffer out
+ while (audioClient.CurrentPadding > 0)
+ {
+ await Task.Delay(latencyMilliseconds / 2);
+ }
+ audioClient.Stop();
+ isClientRunning = false;
+ audioClient.Reset();
+ playbackState = WasapiOutState.Stopped;
+ RaisePlaybackStopped(null);
+ }
+ if (playbackState == WasapiOutState.Disposing)
+ {
+ audioClient.Stop();
+ isClientRunning = false;
+ audioClient.Reset();
+ playbackState = WasapiOutState.Disposed;
+ var disposablePlaybackProvider = playbackProvider as IDisposable;
+ if (disposablePlaybackProvider!=null)
+ disposablePlaybackProvider.Dispose(); // do everything on this thread, even dispose in case it is Media Foundation
+ RaisePlaybackStopped(null);
+
+ }
+
+ }
+ }
+ catch (Exception e)
+ {
+ RaisePlaybackStopped(e);
+ }
+ finally
+ {
+ audioClient.Dispose();
+ audioClient = null;
+ renderClient = null;
+ NativeMethods.CloseHandle(frameEventWaitHandle);
+
+ }
+ }
+
+ private void RaisePlaybackStopped(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+
+ private void FillBuffer(IWaveProvider playbackProvider, int frameCount)
+ {
+ IntPtr buffer = renderClient.GetBuffer(frameCount);
+ int readLength = frameCount*bytesPerFrame;
+ int read = playbackProvider.Read(readBuffer, 0, readLength);
+ if (read == 0)
+ {
+ playbackState = WasapiOutState.Stopping;
+ }
+ Marshal.Copy(readBuffer, 0, buffer, read);
+ int actualFrameCount = read/bytesPerFrame;
+ /*if (actualFrameCount != frameCount)
+ {
+ Debug.WriteLine(String.Format("WASAPI wanted {0} frames, supplied {1}", frameCount, actualFrameCount ));
+ }*/
+ renderClient.ReleaseBuffer(actualFrameCount, AudioClientBufferFlags.None);
+ }
+
+ #region IWavePlayer Members
+
+ ///
+ /// Begin Playback
+ ///
+ public void Play()
+ {
+ if (playbackState != WasapiOutState.Playing)
+ {
+ playbackState = WasapiOutState.Playing;
+ playThreadEvent.Set();
+ }
+ }
+
+ ///
+ /// Stop playback and flush buffers
+ ///
+ public void Stop()
+ {
+ if (playbackState == WasapiOutState.Playing || playbackState == WasapiOutState.Paused)
+ {
+ playbackState = WasapiOutState.Stopping;
+ playThreadEvent.Set();
+ }
+ }
+
+ ///
+ /// Stop playback without flushing buffers
+ ///
+ public void Pause()
+ {
+ if (playbackState == WasapiOutState.Playing)
+ {
+ playbackState = WasapiOutState.Paused;
+ playThreadEvent.Set();
+ }
+ }
+
+ ///
+ /// Old init implementation. Use the func one
+ ///
+ ///
+ ///
+ [Obsolete]
+ public async Task Init(IWaveProvider provider)
+ {
+ Init(() => provider);
+ }
+
+ ///
+ /// Initializes with a function to create the provider that is made on the playback thread
+ ///
+ /// Creates the wave provider
+ public void Init(Func waveProviderFunc)
+ {
+ if (isInitialized) throw new InvalidOperationException("Already Initialized");
+ isInitialized = true;
+ this.waveProviderFunc = waveProviderFunc;
+ ThreadPool.RunAsync(s => PlayThread());
+ }
+
+ ///
+ /// Initialize for playing the specified wave stream
+ ///
+ private IWaveProvider Init()
+ {
+ var waveProvider = waveProviderFunc();
+ long latencyRefTimes = latencyMilliseconds*10000;
+ outputFormat = waveProvider.WaveFormat;
+ // first attempt uses the WaveFormat from the WaveStream
+ WaveFormatExtensible closestSampleRateFormat;
+ if (!audioClient.IsFormatSupported(shareMode, outputFormat, out closestSampleRateFormat))
+ {
+ // Use closesSampleRateFormat (in sharedMode, it equals usualy to the audioClient.MixFormat)
+ // See documentation : http://msdn.microsoft.com/en-us/library/ms678737(VS.85).aspx
+ // They say : "In shared mode, the audio engine always supports the mix format"
+ // The MixFormat is more likely to be a WaveFormatExtensible.
+ if (closestSampleRateFormat == null)
+ {
+ WaveFormat correctSampleRateFormat = audioClient.MixFormat;
+ /*WaveFormat.CreateIeeeFloatWaveFormat(
+ audioClient.MixFormat.SampleRate,
+ audioClient.MixFormat.Channels);*/
+
+ if (!audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ // Iterate from Worst to Best Format
+ WaveFormatExtensible[] bestToWorstFormats =
+ {
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 32,
+ outputFormat.Channels),
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 24,
+ outputFormat.Channels),
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 16,
+ outputFormat.Channels),
+ };
+
+ // Check from best Format to worst format ( Float32, Int24, Int16 )
+ for (int i = 0; i < bestToWorstFormats.Length; i++)
+ {
+ correctSampleRateFormat = bestToWorstFormats[i];
+ if (audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ break;
+ }
+ correctSampleRateFormat = null;
+ }
+
+ // If still null, then test on the PCM16, 2 channels
+ if (correctSampleRateFormat == null)
+ {
+ // Last Last Last Chance (Thanks WASAPI)
+ correctSampleRateFormat = new WaveFormatExtensible(outputFormat.SampleRate, 16, 2);
+ if (!audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ throw new NotSupportedException("Can't find a supported format to use");
+ }
+ }
+ }
+ outputFormat = correctSampleRateFormat;
+ }
+ else
+ {
+ outputFormat = closestSampleRateFormat;
+ }
+
+ // just check that we can make it.
+ //using (new MediaFoundationResampler(waveProvider, outputFormat))
+ {
+ }
+ this.resamplerNeeded = true;
+ }
+ else
+ {
+ resamplerNeeded = false;
+ }
+
+ // Init Shared or Exclusive
+ if (shareMode == AudioClientShareMode.Shared)
+ {
+ // With EventCallBack and Shared,
+ audioClient.Initialize(shareMode, AudioClientStreamFlags.EventCallback, latencyRefTimes, 0,
+ outputFormat, Guid.Empty);
+
+ // Get back the effective latency from AudioClient
+ latencyMilliseconds = (int) (audioClient.StreamLatency/10000);
+ }
+ else
+ {
+ // With EventCallBack and Exclusive, both latencies must equals
+ audioClient.Initialize(shareMode, AudioClientStreamFlags.EventCallback, latencyRefTimes, latencyRefTimes,
+ outputFormat, Guid.Empty);
+ }
+
+ // Create the Wait Event Handle
+ frameEventWaitHandle = NativeMethods.CreateEventEx(IntPtr.Zero, IntPtr.Zero, 0, EventAccess.EVENT_ALL_ACCESS);
+ audioClient.SetEventHandle(frameEventWaitHandle);
+
+ // Get the RenderClient
+ renderClient = audioClient.AudioRenderClient;
+ return waveProvider;
+ }
+
+ ///
+ /// Playback State
+ ///
+ public PlaybackState PlaybackState
+ {
+ get
+ {
+ switch (playbackState)
+ {
+ case WasapiOutState.Playing:
+ return PlaybackState.Playing;
+ case WasapiOutState.Paused:
+ return PlaybackState.Paused;
+ default:
+ return PlaybackState.Stopped;
+ }
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (audioClient != null)
+ {
+ playbackState = WasapiOutState.Disposing;
+ playThreadEvent.Set();
+ }
+ }
+ }
+
+ ///
+ /// Come useful native methods for Windows 8 support
+ ///
+ class NativeMethods
+ {
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = false, PreserveSig = true,
+ SetLastError = true)]
+ internal static extern IntPtr CreateEventEx(IntPtr lpEventAttributes, IntPtr lpName, int dwFlags,
+ EventAccess dwDesiredAccess);
+
+
+ [DllImport("kernel32.dll", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
+ public static extern bool CloseHandle(IntPtr hObject);
+
+ [DllImport("kernel32", ExactSpelling = true, PreserveSig = true, SetLastError = true)]
+ public static extern int WaitForSingleObjectEx(IntPtr hEvent, int milliseconds, bool bAlertable);
+
+ ///
+ /// Enables Windows Store apps to access preexisting Component Object Model (COM) interfaces in the WASAPI family.
+ ///
+ /// A device interface ID for an audio device. This is normally retrieved from a DeviceInformation object or one of the methods of the MediaDevice class.
+ /// The IID of a COM interface in the WASAPI family, such as IAudioClient.
+ /// Interface-specific activation parameters. For more information, see the pActivationParams parameter in IMMDevice::Activate.
+ ///
+ ///
+ [DllImport("Mmdevapi.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void ActivateAudioInterfaceAsync(
+ [In, MarshalAs(UnmanagedType.LPWStr)] string deviceInterfacePath,
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
+ [In] IntPtr activationParams, // n.b. is actually a pointer to a PropVariant, but we never need to pass anything but null
+ [In] IActivateAudioInterfaceCompletionHandler completionHandler,
+ out IActivateAudioInterfaceAsyncOperation activationOperation);
+ }
+
+ // trying some ideas from Lucian Wischik (ljw1004):
+ // http://www.codeproject.com/Articles/460145/Recording-and-playing-PCM-audio-on-Windows-8-VB
+
+ [Flags]
+ internal enum EventAccess
+ {
+ STANDARD_RIGHTS_REQUIRED = 0xF0000,
+ SYNCHRONIZE = 0x100000,
+ EVENT_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3
+ }
+
+ internal class ActivateAudioInterfaceCompletionHandler :
+ IActivateAudioInterfaceCompletionHandler, IAgileObject
+ {
+ private Action initializeAction;
+ private TaskCompletionSource tcs = new TaskCompletionSource();
+
+ public ActivateAudioInterfaceCompletionHandler(
+ Action initializeAction)
+ {
+ this.initializeAction = initializeAction;
+ }
+
+ public void ActivateCompleted(IActivateAudioInterfaceAsyncOperation activateOperation)
+ {
+ // First get the activation results, and see if anything bad happened then
+ int hr = 0;
+ object unk = null;
+ activateOperation.GetActivateResult(out hr, out unk);
+ if (hr != 0)
+ {
+ tcs.TrySetException(Marshal.GetExceptionForHR(hr, new IntPtr(-1)));
+ return;
+ }
+
+ var pAudioClient = (IAudioClient2) unk;
+
+ // Next try to call the client's (synchronous, blocking) initialization method.
+ try
+ {
+ initializeAction(pAudioClient);
+ tcs.SetResult(pAudioClient);
+ }
+ catch (Exception ex)
+ {
+ tcs.TrySetException(ex);
+ }
+
+
+ }
+
+
+ public TaskAwaiter GetAwaiter()
+ {
+ return tcs.Task.GetAwaiter();
+ }
+ }
+
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("41D949AB-9862-444A-80F6-C261334DA5EB")]
+ interface IActivateAudioInterfaceCompletionHandler
+ {
+ //virtual HRESULT STDMETHODCALLTYPE ActivateCompleted(/*[in]*/ _In_
+ // IActivateAudioInterfaceAsyncOperation *activateOperation) = 0;
+ void ActivateCompleted(IActivateAudioInterfaceAsyncOperation activateOperation);
+ }
+
+
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("72A22D78-CDE4-431D-B8CC-843A71199B6D")]
+ interface IActivateAudioInterfaceAsyncOperation
+ {
+ //virtual HRESULT STDMETHODCALLTYPE GetActivateResult(/*[out]*/ _Out_
+ // HRESULT *activateResult, /*[out]*/ _Outptr_result_maybenull_ IUnknown **activatedInterface) = 0;
+ void GetActivateResult([Out] out int activateResult,
+ [Out, MarshalAs(UnmanagedType.IUnknown)] out object activateInterface);
+ }
+
+
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("726778CD-F60A-4eda-82DE-E47610CD78AA")]
+ interface IAudioClient2
+ {
+ [PreserveSig]
+ int Initialize(AudioClientShareMode shareMode,
+ AudioClientStreamFlags streamFlags,
+ long hnsBufferDuration, // REFERENCE_TIME
+ long hnsPeriodicity, // REFERENCE_TIME
+ [In] WaveFormat pFormat,
+ [In] IntPtr audioSessionGuid);
+
+ // ref Guid AudioSessionGuid
+
+ ///
+ /// The GetBufferSize method retrieves the size (maximum capacity) of the endpoint buffer.
+ ///
+ int GetBufferSize(out uint bufferSize);
+
+ [return: MarshalAs(UnmanagedType.I8)]
+ long GetStreamLatency();
+
+ int GetCurrentPadding(out int currentPadding);
+
+ [PreserveSig]
+ int IsFormatSupported(
+ AudioClientShareMode shareMode,
+ [In] WaveFormat pFormat,
+ [Out, MarshalAs(UnmanagedType.LPStruct)] out WaveFormatExtensible closestMatchFormat);
+
+ int GetMixFormat(out IntPtr deviceFormatPointer);
+
+ // REFERENCE_TIME is 64 bit int
+ int GetDevicePeriod(out long defaultDevicePeriod, out long minimumDevicePeriod);
+
+ int Start();
+
+ int Stop();
+
+ int Reset();
+
+ int SetEventHandle(IntPtr eventHandle);
+
+ ///
+ /// The GetService method accesses additional services from the audio client object.
+ ///
+ /// The interface ID for the requested service.
+ /// Pointer to a pointer variable into which the method writes the address of an instance of the requested interface.
+ [PreserveSig]
+ int GetService([In, MarshalAs(UnmanagedType.LPStruct)] Guid interfaceId,
+ [Out, MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer);
+
+ //virtual HRESULT STDMETHODCALLTYPE IsOffloadCapable(/*[in]*/ _In_
+ // AUDIO_STREAM_CATEGORY Category, /*[in]*/ _Out_ BOOL *pbOffloadCapable) = 0;
+ void IsOffloadCapable(int category, out bool pbOffloadCapable);
+ //virtual HRESULT STDMETHODCALLTYPE SetClientProperties(/*[in]*/ _In_
+ // const AudioClientProperties *pProperties) = 0;
+ void SetClientProperties([In] IntPtr pProperties);
+ // TODO: try this: void SetClientProperties([In, MarshalAs(UnmanagedType.LPStruct)] AudioClientProperties pProperties);
+ //virtual HRESULT STDMETHODCALLTYPE GetBufferSizeLimits(/*[in]*/ _In_
+ // const WAVEFORMATEX *pFormat, /*[in]*/ _In_ BOOL bEventDriven, /*[in]*/
+ // _Out_ REFERENCE_TIME *phnsMinBufferDuration, /*[in]*/ _Out_
+ // REFERENCE_TIME *phnsMaxBufferDuration) = 0;
+ void GetBufferSizeLimits(IntPtr pFormat, bool bEventDriven,
+ out long phnsMinBufferDuration, out long phnsMaxBufferDuration);
+ }
+
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("94ea2b94-e9cc-49e0-c0ff-ee64ca8f5b90")]
+ interface IAgileObject
+ {
+
+ }
+
+
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer.cs b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer.cs
new file mode 100644
index 00000000..e2b3e2e9
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer.cs
@@ -0,0 +1,47 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ // wmsbuffer.h
+ [ComImport]
+ [Guid("E1CD3524-03D7-11d2-9EED-006097D2D7CF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface INSSBuffer
+ {
+ void GetLength([Out] out uint pdwLength);
+
+ void SetLength([In] uint dwLength);
+
+ void GetMaxLength([Out] out uint pdwLength);
+
+ void GetBuffer([Out] out IntPtr ppdwBuffer);
+
+ void GetBufferAndLength([Out] out IntPtr ppdwBuffer, [Out] out uint pdwLength);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer2.cs b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer2.cs
new file mode 100644
index 00000000..ab1081cc
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer2.cs
@@ -0,0 +1,47 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("4F528693-1035-43fe-B428-757561AD3A68")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface INSSBuffer2 : INSSBuffer
+ {
+ //INSSBuffer
+ new void GetLength([Out] out uint pdwLength);
+ new void SetLength([In] uint dwLength);
+ new void GetMaxLength([Out] out uint pdwLength);
+ new void GetBuffer([Out] out IntPtr ppdwBuffer);
+ new void GetBufferAndLength([Out] out IntPtr ppdwBuffer, [Out] out uint pdwLength);
+ //INSSBuffer2
+ void GetSampleProperties([In] uint cbProperties, [Out] out byte pbProperties);
+
+ void SetSampleProperties([In] uint cbProperties, [In] ref byte pbProperties);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer3.cs b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer3.cs
new file mode 100644
index 00000000..168d4f17
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer3.cs
@@ -0,0 +1,54 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("C87CEAAF-75BE-4bc4-84EB-AC2798507672")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface INSSBuffer3 : INSSBuffer2
+ {
+ //INSSBuffer
+ new void GetLength([Out] out uint pdwLength);
+ new void SetLength([In] uint dwLength);
+ new void GetMaxLength([Out] out uint pdwLength);
+ new void GetBuffer([Out] out IntPtr ppdwBuffer);
+ new void GetBufferAndLength([Out] out IntPtr ppdwBuffer, [Out] out uint pdwLength);
+ //INSSBuffer2
+ new void GetSampleProperties([In] uint cbProperties, [Out] out byte pbProperties);
+ new void SetSampleProperties([In] uint cbProperties, [In] ref byte pbProperties);
+ //INSSBuffer3
+ void SetProperty([In] Guid guidBufferProperty,
+ [In] IntPtr pvBufferProperty,
+ [In] uint dwBufferPropertySize);
+
+ void GetProperty([In] Guid guidBufferProperty,
+ /*out]*/ IntPtr pvBufferProperty,
+ [In, Out] ref uint pdwBufferPropertySize);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer4.cs b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer4.cs
new file mode 100644
index 00000000..70b202fc
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/BufferTypes/INSSBuffer4.cs
@@ -0,0 +1,60 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("B6B8FD5A-32E2-49d4-A910-C26CC85465ED")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface INSSBuffer4 : INSSBuffer3
+ {
+ //INSSBuffer
+ new void GetLength([Out] out uint pdwLength);
+ new void SetLength([In] uint dwLength);
+ new void GetMaxLength([Out] out uint pdwLength);
+ new void GetBuffer([Out] out IntPtr ppdwBuffer);
+ new void GetBufferAndLength([Out] out IntPtr ppdwBuffer, [Out] out uint pdwLength);
+ //INSSBuffer2
+ new void GetSampleProperties([In] uint cbProperties, [Out] out byte pbProperties);
+ new void SetSampleProperties([In] uint cbProperties, [In] ref byte pbProperties);
+ //INSSBuffer3
+ new void SetProperty([In] Guid guidBufferProperty,
+ [In] IntPtr pvBufferProperty,
+ [In] uint dwBufferPropertySize);
+ new void GetProperty([In] Guid guidBufferProperty,
+ /*out]*/ IntPtr pvBufferProperty,
+ [In, Out] ref uint pdwBufferPropertySize);
+ //INSSBuffer4
+ void GetPropertyCount([Out] out uint pcBufferProperties);
+
+ void GetPropertyByIndex([In] uint dwBufferPropertyIndex,
+ [Out] out Guid pguidBufferProperty,
+ /*[out]*/ IntPtr pvBufferProperty,
+ [In, Out] ref uint pdwBufferPropertySize);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/BufferTypes/IWMSBufferAllocator.cs b/NAudio.WindowsMediaFormat/Interop/BufferTypes/IWMSBufferAllocator.cs
new file mode 100644
index 00000000..b7c1c191
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/BufferTypes/IWMSBufferAllocator.cs
@@ -0,0 +1,43 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("61103CA4-2033-11d2-9EF1-006097D2D7CF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMSBufferAllocator
+ {
+
+ void AllocateBuffer([In] uint dwMaxBufferSize,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppBuffer);
+
+ void AllocatePageSizeBuffer([In] uint dwMaxBufferSize,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppBuffer);
+ };
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/Codec.cs b/NAudio.WindowsMediaFormat/Interop/Codec.cs
new file mode 100644
index 00000000..1c81985c
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/Codec.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public class Codec
+ {
+ public IWMCodecInfo2 CodecInformation { get; set; }
+ public Guid MediaType {get;set;}
+
+ public string Name
+ {
+ get
+ {
+ StringBuilder name;
+ int namelen = 0;
+
+ CodecInformation.GetCodecName(MediaType, Index, null, ref namelen);
+ name = new StringBuilder(namelen);
+ CodecInformation.GetCodecName(MediaType, Index, name, ref namelen);
+ return name.ToString();
+ }
+ }
+
+ public int Index {get; private set;}
+ public CodecFormat[] CodecFormats { get; private set;}
+
+ public Codec(IWMCodecInfo2 codecInfo, int index, Guid mediaType)
+ {
+ CodecInformation = codecInfo;
+ Index = index;
+ MediaType = mediaType;
+
+ CodecFormats = CodecFormat.GetMediaFormats(this);
+ }
+
+ ///
+ /// Gets all Windows media Codecs.
+ ///
+ /// MediaTypes WMMEDIATYPE_Audio or WMMEDIATYPE_Video expected
+ public static Codec[] GetCodecs(Guid mediaType)
+ {
+
+ IWMCodecInfo2 codecInfo = (IWMCodecInfo2)WM.CreateProfileManager();
+
+ int count;
+ codecInfo.GetCodecInfoCount(mediaType, out count);
+ var list = new Codec[count];
+ for (int i = 0; i < count; i++)
+ list[i] = new Codec(codecInfo,i,mediaType);
+
+ return list;
+ }
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CodecFormat.cs b/NAudio.WindowsMediaFormat/Interop/CodecFormat.cs
new file mode 100644
index 00000000..bd933b42
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CodecFormat.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public class CodecFormat
+ {
+ public Codec Codec {get;private set;}
+ public WMStreamConfig StreamConfig { get; private set; }
+ public int Index { get; private set; }
+ public string Description
+ {
+ get
+ {
+ StringBuilder name;
+ int namelen = 0;
+ Guid mediaType = Codec.MediaType;
+ IWMStreamConfig config;
+
+ Codec.CodecInformation.GetCodecFormatDesc(mediaType, (int)Codec.Index, Index, out config, null, ref namelen);
+ name = new StringBuilder(namelen);
+ Codec.CodecInformation.GetCodecFormatDesc(mediaType, (int)Codec.Index, Index, out config, name, ref namelen);
+ return name.ToString();
+ }
+ }
+
+ public CodecFormat(Codec codec, WMStreamConfig wmStreamConfig, int index)
+ {
+ Codec = codec;
+ StreamConfig = wmStreamConfig;
+ Index = index;
+ }
+
+ public IWMProfile GetProfile()
+ {
+ IWMProfile profile;
+ ((IWMProfileManager)Codec.CodecInformation).CreateEmptyProfile(WMT_VERSION.WMT_VER_9_0, out profile);
+
+ StreamConfig.StreamNumber = 1;
+
+ profile.AddStream(StreamConfig.StreamConfig);
+
+ return profile;
+ }
+
+
+ ///
+ /// Gets all media formats for a codec.
+ ///
+ /// Codec
+ /// All media formats for the specified codec
+ public static CodecFormat[] GetMediaFormats(Codec codec)
+ {
+ var codecInfo = codec.CodecInformation;
+
+ Guid mediaType = codec.MediaType;
+ int formatCount;
+ codecInfo.GetCodecFormatCount(mediaType, codec.Index, out formatCount);
+
+ var formats = new CodecFormat[formatCount];
+ for (int i = 0; i < formatCount; i++)
+ {
+ IWMStreamConfig config;
+ codecInfo.GetCodecFormat(mediaType , codec.Index, i, out config);
+ WMStreamConfig stream = new WMStreamConfig(config);
+ formats[i] = new CodecFormat(codec, stream, (int)i);
+ }
+
+ return formats;
+ }
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMBandwidthSharing.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMBandwidthSharing.cs
new file mode 100644
index 00000000..b48b2471
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMBandwidthSharing.cs
@@ -0,0 +1,50 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("AD694AF1-F8D9-42F8-BC47-70311B0C4F9E")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMBandwidthSharing : IWMStreamList
+ {
+ //IWMStreamList
+ new void GetStreams([Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwStreamNumArray,
+ [In, Out] ref ushort pcStreams);
+ new void AddStream([In] ushort wStreamNum);
+ new void RemoveStream([In] ushort wStreamNum);
+ //IWMBandwidthSharing
+ void GetType([Out] out Guid pguidType);
+
+ void SetType([In] ref Guid guidType);
+
+ void GetBandwidth([Out] out uint pdwBitrate, [Out] out uint pmsBufferWindow);
+
+ void SetBandwidth([In] uint dwBitrate, [In] uint msBufferWindow);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo.cs
new file mode 100644
index 00000000..3dd52b1b
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComVisible(true), ComImport,
+Guid("A970F41E-34DE-4a98-B3BA-E4B3CA7528F0"),
+InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMCodecInfo
+ {
+ void GetCodecInfoCount(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [Out] out int pcCodecs);
+
+ void GetCodecFormatCount(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [Out] out int pcFormat);
+
+ void GetCodecFormat(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In] int dwFormatIndex,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppIStreamConfig);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo2.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo2.cs
new file mode 100644
index 00000000..f4c9be0e
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo2.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport, SuppressUnmanagedCodeSecurity,
+ Guid("AA65E273-B686-4056-91EC-DD768D4DF710"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMCodecInfo2 : IWMCodecInfo
+ {
+ #region IWMCodecInfo Methods
+
+ new void GetCodecInfoCount(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [Out] out int pcCodecs
+ );
+
+ new void GetCodecFormatCount(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [Out] out int pcFormat
+ );
+
+ new void GetCodecFormat(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In] int dwFormatIndex,
+ out IWMStreamConfig ppIStreamConfig
+ );
+
+ #endregion
+
+ void GetCodecName(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [Out] StringBuilder wszName,
+ ref int pcchName
+ );
+
+ void GetCodecFormatDesc(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In] int dwFormatIndex,
+ out IWMStreamConfig ppIStreamConfig,
+ [Out] StringBuilder wszDesc,
+ ref int pcchDesc
+ );
+ }
+
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo3 .cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo3 .cs
new file mode 100644
index 00000000..15cdbc32
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMCodecInfo3 .cs
@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport, SuppressUnmanagedCodeSecurity,
+ Guid("7E51F487-4D93-4F98-8AB4-27D0565ADC51"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMCodecInfo3 : IWMCodecInfo2
+ {
+ #region IWMCodecInfo Methods
+
+ new void GetCodecInfoCount(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ out int pcCodecs
+ );
+
+ new void GetCodecFormatCount(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ out int pcFormat
+ );
+
+ new void GetCodecFormat(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In] int dwFormatIndex,
+ out IWMStreamConfig ppIStreamConfig
+ );
+
+ #endregion
+
+ #region IWMCodecInfo2 Methods
+
+ new void GetCodecName(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [Out] StringBuilder wszName,
+ ref int pcchName
+ );
+
+ new void GetCodecFormatDesc(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In] int dwFormatIndex,
+ out IWMStreamConfig ppIStreamConfig,
+ [Out] StringBuilder wszDesc,
+ ref int pcchDesc
+ );
+
+ #endregion
+
+ void GetCodecFormatProp(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In] int dwFormatIndex,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ ref int pdwSize
+ );
+
+ void GetCodecProp(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ ref int pdwSize
+ );
+
+ void SetCodecEnumerationSetting(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ [In] int dwSize
+ );
+
+ void GetCodecEnumerationSetting(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidType,
+ [In] int dwCodecIndex,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ ref int pdwSize
+ );
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo.cs
new file mode 100644
index 00000000..0d734785
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo.cs
@@ -0,0 +1,86 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BDA-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMHeaderInfo
+ {
+
+ void GetAttributeCount([In] ushort wStreamNum, [Out] out ushort pcAttributes);
+
+ void GetAttributeByIndex([In] ushort wIndex,
+ [In, Out] ref ushort pwStreamNum,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchNameLen,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ IntPtr pValue,
+ [In, Out] ref ushort pcbLength);
+
+ void GetAttributeByName([In, Out] ref ushort pwStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ IntPtr pValue,
+ [In, Out] ref ushort pcbLength);
+
+ void SetAttribute([In] ushort wStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ IntPtr pValue,
+ [In] ushort cbLength);
+
+ void GetMarkerCount([Out] out ushort pcMarkers);
+
+ void GetMarker([In] ushort wIndex,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszMarkerName,
+ [In, Out] ref ushort pcchMarkerNameLen,
+ [Out] out ulong pcnsMarkerTime);
+
+ void AddMarker([In, MarshalAs(UnmanagedType.LPWStr)] string pwszMarkerName,
+ [In] ulong cnsMarkerTime);
+
+ void RemoveMarker([In] ushort wIndex);
+
+ void GetScriptCount([Out] out ushort pcScripts);
+
+ void GetScript([In] ushort wIndex,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszType,
+ [In, Out] ref ushort pcchTypeLen,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszCommand,
+ [In, Out] ref ushort pcchCommandLen,
+ [Out] out ulong pcnsScriptTime);
+
+ void AddScript([In, MarshalAs(UnmanagedType.LPWStr)] string pwszType,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszCommand,
+ [In] ulong cnsScriptTime);
+
+ void RemoveScript([In] ushort wIndex);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo2.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo2.cs
new file mode 100644
index 00000000..c2504b59
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo2.cs
@@ -0,0 +1,86 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("15CF9781-454E-482e-B393-85FAE487A810")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMHeaderInfo2 : IWMHeaderInfo
+ {
+ //IWMHeaderInfo
+ new void GetAttributeCount([In] ushort wStreamNum, [Out] out ushort pcAttributes);
+ new void GetAttributeByIndex([In] ushort wIndex,
+ [In, Out] ref ushort pwStreamNum,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchNameLen,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ IntPtr pValue,
+ [In, Out] ref ushort pcbLength);
+ new void GetAttributeByName([In, Out] ref ushort pwStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ IntPtr pValue,
+ [In, Out] ref ushort pcbLength);
+ new void SetAttribute([In] ushort wStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ IntPtr pValue,
+ [In] ushort cbLength);
+ new void GetMarkerCount([Out] out ushort pcMarkers);
+ new void GetMarker([In] ushort wIndex,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszMarkerName,
+ [In, Out] ref ushort pcchMarkerNameLen,
+ [Out] out ulong pcnsMarkerTime);
+ new void AddMarker([In, MarshalAs(UnmanagedType.LPWStr)] string pwszMarkerName,
+ [In] ulong cnsMarkerTime);
+ new void RemoveMarker([In] ushort wIndex);
+ new void GetScriptCount([Out] out ushort pcScripts);
+ new void GetScript([In] ushort wIndex,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszType,
+ [In, Out] ref ushort pcchTypeLen,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszCommand,
+ [In, Out] ref ushort pcchCommandLen,
+ [Out] out ulong pcnsScriptTime);
+ new void AddScript([In, MarshalAs(UnmanagedType.LPWStr)] string pwszType,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszCommand,
+ [In] ulong cnsScriptTime);
+ new void RemoveScript([In] ushort wIndex);
+ //IWMHeaderInfo2
+ void GetCodecInfoCount([Out] out uint pcCodecInfos);
+
+ void GetCodecInfo([In] uint wIndex,
+ [In, Out] ref ushort pcchName,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchDescription,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszDescription,
+ [Out] out WMT_CODEC_INFO_TYPE pCodecType,
+ [In, Out] ref ushort pcbCodecInfo,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbCodecInfo);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo3.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo3.cs
new file mode 100644
index 00000000..c4937fdc
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMHeaderInfo3.cs
@@ -0,0 +1,125 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("15CC68E3-27CC-4ecd-B222-3F5D02D80BD5")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMHeaderInfo3 : IWMHeaderInfo2
+ {
+ //IWMHeaderInfo
+ new void GetAttributeCount([In] ushort wStreamNum, [Out] out ushort pcAttributes);
+ new void GetAttributeByIndex([In] ushort wIndex,
+ [In, Out] ref ushort pwStreamNum,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchNameLen,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ IntPtr pValue,
+ [In, Out] ref ushort pcbLength);
+ new void GetAttributeByName([In, Out] ref ushort pwStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ IntPtr pValue,
+ [In, Out] ref ushort pcbLength);
+ new void SetAttribute([In] ushort wStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ IntPtr pValue,
+ [In] ushort cbLength);
+ new void GetMarkerCount([Out] out ushort pcMarkers);
+ new void GetMarker([In] ushort wIndex,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszMarkerName,
+ [In, Out] ref ushort pcchMarkerNameLen,
+ [Out] out ulong pcnsMarkerTime);
+ new void AddMarker([In, MarshalAs(UnmanagedType.LPWStr)] string pwszMarkerName,
+ [In] ulong cnsMarkerTime);
+ new void RemoveMarker([In] ushort wIndex);
+ new void GetScriptCount([Out] out ushort pcScripts);
+ new void GetScript([In] ushort wIndex,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszType,
+ [In, Out] ref ushort pcchTypeLen,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszCommand,
+ [In, Out] ref ushort pcchCommandLen,
+ [Out] out ulong pcnsScriptTime);
+ new void AddScript([In, MarshalAs(UnmanagedType.LPWStr)] string pwszType,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszCommand,
+ [In] ulong cnsScriptTime);
+ new void RemoveScript([In] ushort wIndex);
+ //IWMHeaderInfo2
+ new void GetCodecInfoCount([Out] out uint pcCodecInfos);
+ new void GetCodecInfo([In] uint wIndex,
+ [In, Out] ref ushort pcchName,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchDescription,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszDescription,
+ [Out] out WMT_CODEC_INFO_TYPE pCodecType,
+ [In, Out] ref ushort pcbCodecInfo,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbCodecInfo);
+ //IWMHeaderInfo3
+ void GetAttributeCountEx([In] ushort wStreamNum, [Out] out ushort pcAttributes);
+
+ void GetAttributeIndices([In] ushort wStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszName,
+ /* DWORD* */IntPtr pwLangIndex,
+ [Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwIndices,
+ [In, Out] ref ushort pwCount);
+
+ void GetAttributeByIndexEx([In] ushort wStreamNum,
+ [In] ushort wIndex,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pwNameLen,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ [Out] out ushort pwLangIndex,
+ IntPtr pValue,
+ [In, Out] ref uint pdwDataLength);
+
+ void ModifyAttribute([In] ushort wStreamNum,
+ [In] ushort wIndex,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In] ushort wLangIndex,
+ IntPtr pValue,
+ [In] uint dwLength);
+
+ void AddAttribute([In] ushort wStreamNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out ushort pwIndex,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In] ushort wLangIndex,
+ IntPtr pValue,
+ [In] uint dwLength);
+
+ void DeleteAttribute([In] ushort wStreamNum, [In] ushort wIndex);
+
+ void AddCodecInfo([In, MarshalAs(UnmanagedType.LPWStr)] string pwszName,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszDescription,
+ [In] WMT_CODEC_INFO_TYPE codecType,
+ [In] ushort cbCodecInfo,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] pbCodecInfo);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMIndexer.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMIndexer.cs
new file mode 100644
index 00000000..cf7db35f
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMIndexer.cs
@@ -0,0 +1,43 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("6d7cdc71-9888-11d3-8edc-00c04f6109cf")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMIndexer
+ {
+
+ void StartIndexing([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMStatusCallback pCallback,
+ [In] IntPtr pvContext);
+
+ void Cancel();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMIndexer2.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMIndexer2.cs
new file mode 100644
index 00000000..36dafb2c
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMIndexer2.cs
@@ -0,0 +1,48 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+
+ [ComImport]
+ [Guid("B70F1E42-6255-4df0-A6B9-02B212D9E2BB")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMIndexer2 : IWMIndexer
+ {
+ //IWMIndexer
+ new void StartIndexing([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMStatusCallback pCallback,
+ [In] IntPtr pvContext);
+ new void Cancel();
+ //IWMIndexer2
+ void Configure([In] ushort wStreamNum,
+ [In] WMT_INDEXER_TYPE nIndexerType,
+ [In] IntPtr pvInterval,
+ [In] IntPtr pvIndexType);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMInputMediaProps.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMInputMediaProps.cs
new file mode 100644
index 00000000..b68f43f9
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMInputMediaProps.cs
@@ -0,0 +1,49 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ // wmsdkidl.h
+ [ComImport]
+ [Guid("96406BD5-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMInputMediaProps : IWMMediaProps
+ {
+ //IWMMediaProps
+ new void GetType([Out] out Guid pguidType);
+ new void GetMediaType( /*[out] WM_MEDIA_TYPE* */ IntPtr pType,
+ [In, Out] ref int pcbType);
+ new void SetMediaType([In] ref WM_MEDIA_TYPE pType);
+ //IWMInputMediaProps
+ void GetConnectionName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchName);
+
+ void GetGroupName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchName);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMediaProps.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMediaProps.cs
new file mode 100644
index 00000000..fd9792a5
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMediaProps.cs
@@ -0,0 +1,45 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ // wmsdkidl.h
+ [ComImport]
+ [Guid("96406BCE-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMMediaProps
+ {
+
+ void GetType([Out] out Guid pguidType);
+
+ void GetMediaType(/*[out] WM_MEDIA_TYPE* */ IntPtr pType,
+ [In, Out] ref int pcbType);
+
+ void SetMediaType([In] ref WM_MEDIA_TYPE pType);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMetadataEditor.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMetadataEditor.cs
new file mode 100644
index 00000000..dc3d8d93
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMetadataEditor.cs
@@ -0,0 +1,43 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BD9-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMMetadataEditor
+ {
+
+ void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+
+ void Close();
+
+ void Flush();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMetadataEditor2.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMetadataEditor2.cs
new file mode 100644
index 00000000..ef396297
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMetadataEditor2.cs
@@ -0,0 +1,44 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [Guid("203CFFE3-2E18-4fdf-B59D-6E71530534CF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMMetadataEditor2 : IWMMetadataEditor
+ {
+ //IWMMetadataEditor
+ new void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ new void Close();
+ new void Flush();
+ //IWMMetadataEditor2
+ void OpenEx([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename,
+ [In] uint dwDesiredAccess,
+ [In] uint dwShareMode);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMutualExclusion.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMutualExclusion.cs
new file mode 100644
index 00000000..db2b6012
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMutualExclusion.cs
@@ -0,0 +1,46 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BDE-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMMutualExclusion : IWMStreamList
+ {
+ //IWMStreamList
+ new void GetStreams([Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwStreamNumArray,
+ [In, Out] ref ushort pcStreams);
+ new void AddStream([In] ushort wStreamNum);
+ new void RemoveStream([In] ushort wStreamNum);
+ //IWMMutualExclusion
+ void GetType([Out] out Guid pguidType);
+
+ void SetType([In] ref Guid guidType);
+ };
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMutualExclusion2.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMutualExclusion2.cs
new file mode 100644
index 00000000..c1de0936
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMMutualExclusion2.cs
@@ -0,0 +1,71 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("0302B57D-89D1-4ba2-85C9-166F2C53EB91")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMMutualExclusion2 : IWMMutualExclusion
+ {
+ //IWMStreamList
+ new void GetStreams([Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwStreamNumArray,
+ [In, Out] ref ushort pcStreams);
+ new void AddStream([In] ushort wStreamNum);
+ new void RemoveStream([In] ushort wStreamNum);
+ //IWMMutualExclusion
+ new void GetType([Out] out Guid pguidType);
+ new void SetType([In] ref Guid guidType);
+ //IWMMutualExclusion2
+ void GetName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchName);
+
+ void SetName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszName);
+
+ void GetRecordCount([Out] out ushort pwRecordCount);
+
+ void AddRecord();
+
+ void RemoveRecord([In] ushort wRecordNumber);
+
+ void GetRecordName([In] ushort wRecordNumber,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszRecordName,
+ [In, Out] ref ushort pcchRecordName);
+
+ void SetRecordName([In] ushort wRecordNumber,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszRecordName);
+
+ void GetStreamsForRecord([In] ushort wRecordNumber,
+ [Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwStreamNumArray,
+ [In, Out] ref ushort pcStreams);
+
+ void AddStreamForRecord([In] ushort wRecordNumber, [In] ushort wStreamNumber);
+
+ void RemoveStreamForRecord([In] ushort wRecordNumber, [In] ushort wStreamNumber);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMOutputMediaProps.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMOutputMediaProps.cs
new file mode 100644
index 00000000..12db5268
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMOutputMediaProps.cs
@@ -0,0 +1,49 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ // wmsdkidl.h
+ [ComImport]
+ [Guid("96406BD7-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMOutputMediaProps : IWMMediaProps
+ {
+ //IWMMediaProps
+ new void GetType([Out] out Guid pguidType);
+ new void GetMediaType( /*[out] WM_MEDIA_TYPE* */ IntPtr pType,
+ [In, Out] ref int pcbType);
+ new void SetMediaType([In] ref WM_MEDIA_TYPE pType);
+ //IWMOutputMediaProps
+ void GetStreamGroupName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchName);
+
+ void GetConnectionName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref ushort pcchName);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStatusCallback.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStatusCallback.cs
new file mode 100644
index 00000000..e40b90bb
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStatusCallback.cs
@@ -0,0 +1,44 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ // wmsdkidl.h
+ [ComImport]
+ [Guid("6d7cdc70-9888-11d3-8edc-00c04f6109cf")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMStatusCallback
+ {
+
+ void OnStatus([In] WMT_STATUS Status,
+ [In] IntPtr hr,
+ [In] WMT_ATTR_DATATYPE dwType,
+ [In] IntPtr pValue,
+ [In] IntPtr pvContext);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStreamList.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStreamList.cs
new file mode 100644
index 00000000..e340d744
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStreamList.cs
@@ -0,0 +1,44 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BDD-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMStreamList
+ {
+
+ void GetStreams([Out, MarshalAs(UnmanagedType.LPArray)] ushort[] pwStreamNumArray,
+ [In, Out] ref ushort pcStreams);
+
+ void AddStream([In] ushort wStreamNum);
+
+ void RemoveStream([In] ushort wStreamNum);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStreamPrioritization.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStreamPrioritization.cs
new file mode 100644
index 00000000..2c58f9bc
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMStreamPrioritization.cs
@@ -0,0 +1,43 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("8C1C6090-F9A8-4748-8EC3-DD1108BA1E77")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMStreamPrioritization
+ {
+
+ void GetPriorityRecords([Out, MarshalAs(UnmanagedType.LPArray)] WM_STREAM_PRIORITY_RECORD[] pRecordArray,
+ [In, Out] ref ushort pcRecords);
+
+ void SetPriorityRecords([In, MarshalAs(UnmanagedType.LPArray)] WM_STREAM_PRIORITY_RECORD[] pRecordArray,
+ [In] ushort cRecords);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMVideoMediaProps.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMVideoMediaProps.cs
new file mode 100644
index 00000000..cf1932c1
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/IWMVideoMediaProps.cs
@@ -0,0 +1,50 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BCF-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMVideoMediaProps : IWMMediaProps
+ {
+ //IWMMediaProps
+ new void GetType([Out] out Guid pguidType);
+ new void GetMediaType( /*[out] WM_MEDIA_TYPE* */ IntPtr pType,
+ [In, Out] ref int pcbType);
+ new void SetMediaType([In] ref WM_MEDIA_TYPE pType);
+ //IWMVideoMediaProps
+ void GetMaxKeyFrameSpacing([Out] out long pllTime);
+
+ void SetMaxKeyFrameSpacing([In] long llTime);
+
+ void GetQuality([Out] out uint pdwQuality);
+
+ void SetQuality([In] uint dwQuality);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_ATTR_DATATYPE.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_ATTR_DATATYPE.cs
new file mode 100644
index 00000000..8d4ab609
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_ATTR_DATATYPE.cs
@@ -0,0 +1,46 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_ATTR_DATATYPE
+ {
+ WMT_TYPE_DWORD = 0,
+
+ WMT_TYPE_STRING = 1,
+
+ WMT_TYPE_BINARY = 2,
+
+ WMT_TYPE_BOOL = 3,
+
+ WMT_TYPE_QWORD = 4,
+
+ WMT_TYPE_WORD = 5,
+
+ WMT_TYPE_GUID = 6,
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_CODEC_INFO_TYPE.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_CODEC_INFO_TYPE.cs
new file mode 100644
index 00000000..661eee04
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_CODEC_INFO_TYPE.cs
@@ -0,0 +1,39 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_CODEC_INFO_TYPE : uint
+ {
+
+ WMT_CODECINFO_AUDIO = 0,
+
+ WMT_CODECINFO_VIDEO = 1,
+
+ WMT_CODECINFO_UNKNOWN = 0xFFFFFFFF,
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_INDEXER_TYPE.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_INDEXER_TYPE.cs
new file mode 100644
index 00000000..d51e2e80
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_INDEXER_TYPE.cs
@@ -0,0 +1,39 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_INDEXER_TYPE
+ {
+
+ WMT_IT_PRESENTATION_TIME,
+
+ WMT_IT_FRAME_NUMBERS,
+
+ WMT_IT_TIMECODE
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_INDEX_TYPE.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_INDEX_TYPE.cs
new file mode 100644
index 00000000..4039ba1b
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_INDEX_TYPE.cs
@@ -0,0 +1,38 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_INDEX_TYPE
+ {
+ WMT_IT_NEAREST_DATA_UNIT = 1,
+
+ WMT_IT_NEAREST_OBJECT,
+
+ WMT_IT_NEAREST_CLEAN_POINT
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_NET_PROTOCOL.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_NET_PROTOCOL.cs
new file mode 100644
index 00000000..0de2f722
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_NET_PROTOCOL.cs
@@ -0,0 +1,35 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_NET_PROTOCOL
+ {
+
+ WMT_PROTOCOL_HTTP = 0,
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_OFFSET_FORMAT.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_OFFSET_FORMAT.cs
new file mode 100644
index 00000000..425f6378
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_OFFSET_FORMAT.cs
@@ -0,0 +1,41 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_OFFSET_FORMAT
+ {
+
+ WMT_OFFSET_FORMAT_100NS,
+
+ WMT_OFFSET_FORMAT_FRAME_NUMBERS,
+
+ WMT_OFFSET_FORMAT_PLAYLIST_OFFSET,
+
+ WMT_OFFSET_FORMAT_TIMECODE
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_PLAY_MODE.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_PLAY_MODE.cs
new file mode 100644
index 00000000..c99c0d9f
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_PLAY_MODE.cs
@@ -0,0 +1,41 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_PLAY_MODE
+ {
+
+ WMT_PLAY_MODE_AUTOSELECT = 0,
+
+ WMT_PLAY_MODE_LOCAL = 1,
+
+ WMT_PLAY_MODE_DOWNLOAD = 2,
+
+ WMT_PLAY_MODE_STREAMING = 3,
+ };
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_RIGHTS.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_RIGHTS.cs
new file mode 100644
index 00000000..a816b36d
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_RIGHTS.cs
@@ -0,0 +1,55 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [Flags]
+ public enum WMT_RIGHTS : uint
+ {
+ ///
+ /// This right is not defined in the WMF SDK, I added it to
+ /// play files with no DRM
+ ///
+ WMT_RIGHT_NO_DRM = 0x00000000,
+
+ WMT_RIGHT_PLAYBACK = 0x00000001,
+
+ WMT_RIGHT_COPY_TO_NON_SDMI_DEVICE = 0x00000002,
+
+ WMT_RIGHT_COPY_TO_CD = 0x00000008,
+
+ WMT_RIGHT_COPY_TO_SDMI_DEVICE = 0x00000010,
+
+ WMT_RIGHT_ONE_TIME = 0x00000020,
+
+ WMT_RIGHT_SAVE_STREAM_PROTECTED = 0x00000040,
+
+ WMT_RIGHT_SDMI_TRIGGER = 0x00010000,
+
+ WMT_RIGHT_SDMI_NOMORECOPIES = 0x00020000
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STATUS.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STATUS.cs
new file mode 100644
index 00000000..eacba57e
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STATUS.cs
@@ -0,0 +1,123 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_STATUS
+ {
+
+ WMT_ERROR = 0,
+
+ WMT_OPENED = 1,
+
+ WMT_BUFFERING_START = 2,
+
+ WMT_BUFFERING_STOP = 3,
+
+ WMT_EOF = 4,
+
+ WMT_END_OF_FILE = 4,
+
+ WMT_END_OF_SEGMENT = 5,
+
+ WMT_END_OF_STREAMING = 6,
+
+ WMT_LOCATING = 7,
+
+ WMT_CONNECTING = 8,
+
+ WMT_NO_RIGHTS = 9,
+
+ WMT_MISSING_CODEC = 10,
+
+ WMT_STARTED = 11,
+
+ WMT_STOPPED = 12,
+
+ WMT_CLOSED = 13,
+
+ WMT_STRIDING = 14,
+
+ WMT_TIMER = 15,
+
+ WMT_INDEX_PROGRESS = 16,
+
+ WMT_SAVEAS_START = 17,
+
+ WMT_SAVEAS_STOP = 18,
+
+ WMT_NEW_SOURCEFLAGS = 19,
+
+ WMT_NEW_METADATA = 20,
+
+ WMT_BACKUPRESTORE_BEGIN = 21,
+
+ WMT_SOURCE_SWITCH = 22,
+
+ WMT_ACQUIRE_LICENSE = 23,
+
+ WMT_INDIVIDUALIZE = 24,
+
+ WMT_NEEDS_INDIVIDUALIZATION = 25,
+
+ WMT_NO_RIGHTS_EX = 26,
+
+ WMT_BACKUPRESTORE_END = 27,
+
+ WMT_BACKUPRESTORE_CONNECTING = 28,
+
+ WMT_BACKUPRESTORE_DISCONNECTING = 29,
+
+ WMT_ERROR_WITHURL = 30,
+
+ WMT_RESTRICTED_LICENSE = 31,
+
+ WMT_CLIENT_CONNECT = 32,
+
+ WMT_CLIENT_DISCONNECT = 33,
+
+ WMT_NATIVE_OUTPUT_PROPS_CHANGED = 34,
+
+ WMT_RECONNECT_START = 35,
+
+ WMT_RECONNECT_END = 36,
+
+ WMT_CLIENT_CONNECT_EX = 37,
+
+ WMT_CLIENT_DISCONNECT_EX = 38,
+
+ WMT_SET_FEC_SPAN = 39,
+
+ WMT_PREROLL_READY = 40,
+
+ WMT_PREROLL_COMPLETE = 41,
+
+ WMT_CLIENT_PROPERTIES = 42,
+
+ WMT_LICENSEURL_SIGNATURE_STATE = 43
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STORAGE_FORMAT.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STORAGE_FORMAT.cs
new file mode 100644
index 00000000..0651e4ab
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STORAGE_FORMAT.cs
@@ -0,0 +1,37 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_STORAGE_FORMAT
+ {
+
+ WMT_Storage_Format_MP3,
+
+ WMT_Storage_Format_V1
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STREAM_SELECTION.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STREAM_SELECTION.cs
new file mode 100644
index 00000000..0f957cde
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_STREAM_SELECTION.cs
@@ -0,0 +1,36 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_STREAM_SELECTION
+ {
+ WMT_OFF = 0,
+ WMT_CLEANPOINT_ONLY = 1,
+ WMT_ON = 2,
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_TIMECODE_EXTENSION_DATA.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_TIMECODE_EXTENSION_DATA.cs
new file mode 100644
index 00000000..f9900bb5
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_TIMECODE_EXTENSION_DATA.cs
@@ -0,0 +1,43 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WMT_TIMECODE_EXTENSION_DATA
+ {
+
+ public ushort wRange;
+
+ public uint dwTimecode;
+
+ public uint dwUserbits;
+
+ public uint dwAmFlags;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_TRANSPORT_TYPE.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_TRANSPORT_TYPE.cs
new file mode 100644
index 00000000..224acaf4
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_TRANSPORT_TYPE.cs
@@ -0,0 +1,37 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_TRANSPORT_TYPE
+ {
+
+ WMT_Transport_Type_Unreliable,
+
+ WMT_Transport_Type_Reliable
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_VERSION.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_VERSION.cs
new file mode 100644
index 00000000..5cde8384
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WMT_VERSION.cs
@@ -0,0 +1,41 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ public enum WMT_VERSION
+ {
+
+ WMT_VER_4_0 = 0x00040000,
+
+ WMT_VER_7_0 = 0x00070000,
+
+ WMT_VER_8_0 = 0x00080000,
+
+ WMT_VER_9_0 = 0x00090000,
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WM_MEDIA_TYPE.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WM_MEDIA_TYPE.cs
new file mode 100644
index 00000000..33d1060c
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WM_MEDIA_TYPE.cs
@@ -0,0 +1,55 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WM_MEDIA_TYPE
+ {
+
+ public Guid majortype;
+
+ public Guid subtype;
+
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool bFixedSizeSamples;
+
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool bTemporalCompression;
+
+ public uint lSampleSize;
+
+ public Guid formattype;
+
+ public IntPtr pUnk;
+
+ public uint cbFormat;
+
+ public IntPtr pbFormat;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/CommonTypes/WM_STREAM_PRIORITY_RECORD.cs b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WM_STREAM_PRIORITY_RECORD.cs
new file mode 100644
index 00000000..862dc776
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/CommonTypes/WM_STREAM_PRIORITY_RECORD.cs
@@ -0,0 +1,40 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WM_STREAM_PRIORITY_RECORD
+ {
+
+ public ushort wStreamNumber;
+
+ [MarshalAs(UnmanagedType.Bool)]
+ public bool fMandatory;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/Enums.cs b/NAudio.WindowsMediaFormat/Interop/Enums.cs
new file mode 100644
index 00000000..89aab5f2
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/Enums.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Runtime.InteropServices;
+
+// additional enums, not sure if they are useful
+namespace NAudio.WindowsMediaFormat
+{
+
+ public enum DRM_HTTP_STATUS
+ {
+ HTTP_NOTINITIATED = 0,
+ HTTP_CONNECTING = 1,
+ HTTP_REQUESTING = 2,
+ HTTP_RECEIVING = 3,
+ HTTP_COMPLETED = 4
+ }
+
+ public enum DRM_INDIVIDUALIZATION_STATUS
+ {
+ INDI_UNDEFINED = 0x0000,
+ INDI_BEGIN = 0x0001,
+ INDI_SUCCEED = 0x0002,
+ INDI_FAIL = 0x0004,
+ INDI_CANCEL = 0x0008,
+ INDI_DOWNLOAD = 0x0010,
+ INDI_INSTALL = 0x0020
+ }
+
+ public enum DRM_LICENSE_STATE_CATEGORY{
+ WM_DRM_LICENSE_STATE_NORIGHT =0,
+ WM_DRM_LICENSE_STATE_UNLIM ,
+ WM_DRM_LICENSE_STATE_COUNT ,
+ WM_DRM_LICENSE_STATE_FROM ,
+ WM_DRM_LICENSE_STATE_UNTIL ,
+ WM_DRM_LICENSE_STATE_FROM_UNTIL ,
+ WM_DRM_LICENSE_STATE_COUNT_FROM ,
+ WM_DRM_LICENSE_STATE_COUNT_UNTIL ,
+ WM_DRM_LICENSE_STATE_COUNT_FROM_UNTIL ,
+ WM_DRM_LICENSE_STATE_EXPIRATION_AFTER_FIRSTUSE // DRM_LICENSE_STATE_CATEGORY
+ }
+
+ public enum NETSOURCE_URLCREDPOLICY_SETTINGS
+ {
+ NETSOURCE_URLCREDPOLICY_SETTING_SILENTLOGONOK =0,
+ NETSOURCE_URLCREDPOLICY_SETTING_MUSTPROMPTUSER =1,
+ NETSOURCE_URLCREDPOLICY_SETTING_ANONYMOUSONLY =2
+ }
+
+ public enum WM_AETYPE
+ {
+ WM_AETYPE_INCLUDE = 'i',
+ WM_AETYPE_EXCLUDE = 'e'
+ }
+
+ public enum WMT_ATTR_IMAGETYPE
+ {
+ WMT_IMAGETYPE_BITMAP = 1,
+ WMT_IMAGETYPE_JPEG = 2,
+ WMT_IMAGETYPE_GIF = 3
+ }
+
+ [Flags]
+ enum WMT_CREDENTIAL_FLAGS
+ {
+ WMT_CREDENTIAL_SAVE = 0x00000001,
+ WMT_CREDENTIAL_DONT_CACHE = 0x00000002,
+ WMT_CREDENTIAL_CLEAR_TEXT = 0x00000004,
+ WMT_CREDENTIAL_PROXY = 0x00000008,
+ WMT_CREDENTIAL_ENCRYPT = 0x00000010
+ }
+
+ enum WMT_DRMLA_TRUST
+ {
+ WMT_DRMLA_UNTRUSTED = 0,
+ WMT_DRMLA_TRUSTED,
+ WMT_DRMLA_TAMPERED
+ }
+
+ enum tagWMT_FILESINK_MODE
+ {
+ WMT_FM_SINGLE_BUFFERS = 1,
+ WMT_FM_FILESINK_DATA_UNITS = 2,
+ WMT_FM_FILESINK_UNBUFFERED = 4
+ }
+
+ enum WMT_IMAGE_TYPE
+ {
+ WMT_IT_NONE = 0,
+ WMT_IT_BITMAP = 1,
+ WMT_IT_JPEG = 2,
+ WMT_IT_GIF = 3
+ }
+
+ enum WMT_MUSICSPEECH_CLASS_MODE
+ {
+ WMT_MS_CLASS_MUSIC = 0,
+ WMT_MS_CLASS_SPEECH = 1,
+ WMT_MS_CLASS_MIXED = 2
+ }
+
+ enum WMT_PROXY_SETTINGS
+ {
+ WMT_PROXY_SETTING_NONE = 0,
+ WMT_PROXY_SETTING_MANUAL = 1,
+ WMT_PROXY_SETTING_AUTO = 2,
+ WMT_PROXY_SETTING_BROWSER = 3,
+ WMT_PROXY_SETTING_MAX = 4
+ }
+
+ enum WMT_WATERMARK_ENTRY_TYPE
+ {
+ WMT_WMETYPE_AUDIO = 1,
+ WMT_WMETYPE_VIDEO = 2
+ }
+
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/MediaTypes.cs b/NAudio.WindowsMediaFormat/Interop/MediaTypes.cs
new file mode 100644
index 00000000..21600f72
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/MediaTypes.cs
@@ -0,0 +1,86 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Media types used to define the format of media used with
+ /// the Windows Media Format SDK
+ ///
+ public static class MediaTypes
+ {
+ public static Guid MEDIASUBTYPE_I420 { get { return new Guid("30323449-0000-0010-8000-00AA00389B71"); } }
+ public static Guid MEDIASUBTYPE_IYUV { get { return new Guid("56555949-0000-0010-8000-00AA00389B71"); } }
+ public static Guid MEDIASUBTYPE_RGB1 { get { return new Guid("E436EB78-524F-11CE-9F53-0020AF0BA770"); } }
+ public static Guid MEDIASUBTYPE_RGB24 { get { return new Guid("E436EB7D-524F-11CE-9F53-0020AF0BA770"); } }
+ public static Guid MEDIASUBTYPE_RGB32 { get { return new Guid("E436EB7E-524F-11CE-9F53-0020AF0BA770"); } }
+ public static Guid MEDIASUBTYPE_RGB4 { get { return new Guid("E436EB79-524F-11CE-9F53-0020AF0BA770"); } }
+ public static Guid MEDIASUBTYPE_RGB555 { get { return new Guid("E436EB7C-524F-11CE-9F53-0020AF0BA770"); } }
+ public static Guid MEDIASUBTYPE_RGB565 { get { return new Guid("E436EB7B-524F-11CE-9F53-0020AF0BA770"); } }
+
+ public static Guid MEDIASUBTYPE_RGB8 { get { return new Guid("E436EB7A-524F-11CE-9F53-0020AF0BA770"); } }
+ public static Guid MEDIASUBTYPE_UYVY { get { return new Guid("59565955-0000-0010-8000-00AA00389B71"); } }
+ public static Guid MEDIASUBTYPE_VIDEOIMAGE { get { return new Guid("1D4A45F2-E5F6-4B44-8388-F0AE5C0E0C37"); } }
+ public static Guid MEDIASUBTYPE_YUY2 { get { return new Guid("32595559-0000-0010-8000-00AA00389B71"); } }
+ public static Guid MEDIASUBTYPE_YV12 { get { return new Guid("31313259-0000-0010-8000-00AA00389B71"); } }
+ public static Guid MEDIASUBTYPE_YVU9 { get { return new Guid("39555659-0000-0010-8000-00AA00389B71"); } }
+ public static Guid MEDIASUBTYPE_YVYU { get { return new Guid("55595659-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMFORMAT_MPEG2Video { get { return new Guid("E06D80E3-DB46-11CF-B4D1-00805F6CBBEA"); } }
+ public static Guid WMFORMAT_Script { get { return new Guid("5C8510F2-DEBE-4CA7-BBA5-F07A104F8DFF"); } }
+ public static Guid WMFORMAT_VideoInfo { get { return new Guid("05589F80-C356-11CE-BF01-00AA0055595A"); } }
+ public static Guid WMFORMAT_WaveFormatEx { get { return new Guid("05589F81-C356-11CE-BF01-00AA0055595A"); } }
+ public static Guid WMFORMAT_WebStream { get { return new Guid("DA1E6B13-8359-4050-B398-388E965BF00C"); } }
+ public static Guid WMMEDIASUBTYPE_ACELPnet { get { return new Guid("00000130-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_Base { get { return new Guid("00000000-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_DRM { get { return new Guid("00000009-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_MP3 { get { return new Guid("00000050-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_MP43 { get { return new Guid("3334504D-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_MP4S { get { return new Guid("5334504D-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_MPEG2_VIDEO { get { return new Guid("E06D8026-DB46-11CF-B4D1-00805F6CBBEA"); } }
+ public static Guid WMMEDIASUBTYPE_MSS1 { get { return new Guid("3153534D-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_MSS2 { get { return new Guid("3253534D-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_PCM { get { return new Guid("00000001-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WebStream { get { return new Guid("776257D4-C627-41CB-8F81-7AC7FF1C40CC"); } }
+ public static Guid WMMEDIASUBTYPE_WMAudio_Lossless { get { return new Guid("00000163-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMAudioV2 { get { return new Guid("00000161-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMAudioV7 { get { return new Guid("00000161-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMAudioV8 { get { return new Guid("00000161-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMAudioV9 { get { return new Guid("00000162-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMSP1 { get { return new Guid("0000000A-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMV1 { get { return new Guid("31564D57-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMV2 { get { return new Guid("32564D57-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMV3 { get { return new Guid("33564D57-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIASUBTYPE_WMVP { get { return new Guid("50564D57-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIATYPE_Audio { get { return new Guid("73647561-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIATYPE_FileTransfer { get { return new Guid("D9E47579-930E-4427-ADFC-AD80F290E470"); } }
+ public static Guid WMMEDIATYPE_Image { get { return new Guid("34A50FD8-8AA5-4386-81FE-A0EFE0488E31"); } }
+ public static Guid WMMEDIATYPE_Script { get { return new Guid("73636D64-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMMEDIATYPE_Text { get { return new Guid("9BBA1EA7-5AB2-4829-BA57-0940209BCF3E"); } }
+ public static Guid WMMEDIATYPE_Video { get { return new Guid("73646976-0000-0010-8000-00AA00389B71"); } }
+ public static Guid WMSCRIPTTYPE_TwoStrings { get { return new Guid("82F38A70-C29F-11D1-97AD-00A0C95EA850"); } }
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/NSSBuffer.cs b/NAudio.WindowsMediaFormat/Interop/NSSBuffer.cs
new file mode 100644
index 00000000..86cf7be1
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/NSSBuffer.cs
@@ -0,0 +1,257 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Helper class who encapsulates INSSBuffer buffers.
+ ///
+ public class NSSBuffer
+ {
+ private INSSBuffer m_Buffer;
+ private uint m_Length;
+ private uint m_MaxLength;
+ private IntPtr m_BufferPtr;
+ private uint m_Position = 0;
+
+ ///
+ /// NSSBuffer constructor
+ ///
+ /// INSSBuffer to wrap
+ public NSSBuffer(INSSBuffer buff)
+ {
+ m_Buffer = buff;
+ m_Buffer.GetBufferAndLength(out m_BufferPtr, out m_Length);
+ m_Buffer.GetMaxLength(out m_MaxLength);
+ }
+
+ ///
+ /// Length of the buffer. Wraps INSSBuffer.GetLength and INSSBuffer.SetLength
+ ///
+ public uint Length
+ {
+ get
+ {
+ return m_Length;
+ }
+ set
+ {
+ if (value <= m_MaxLength)
+ {
+ m_Buffer.SetLength(value);
+ m_Length = value;
+ if (m_Position > m_Length)
+ {
+ m_Position = m_Length;
+ }
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ ///
+ /// Read/Write the position for Read or Write operations
+ ///
+ public uint Position
+ {
+ get
+ {
+ return m_Position;
+ }
+ set
+ {
+ if (value <= m_Length)
+ {
+ m_Position = value;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ ///
+ /// Reference to the internal INSSBuffer
+ ///
+ public INSSBuffer Buffer
+ {
+ get { return m_Buffer; }
+ }
+
+ ///
+ /// Reads from the wrapped buffer to a byte array.
+ /// Position is increased by the number of bytes read.
+ ///
+ /// Destination byte array
+ /// Position in the destination array where to start copying
+ /// Number of bytes to read
+ /// Number of bytes read. Zero means than Position was at buffer length.
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ if (m_Position < m_Length)
+ {
+ IntPtr src = (IntPtr)(m_BufferPtr.ToInt64() + m_Position);
+ int ToCopy = Math.Min(count, (int)(this.Length - this.Position));
+ Marshal.Copy(src, buffer, offset, ToCopy);
+ m_Position += (uint)ToCopy;
+ return ToCopy;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ ///
+ /// Write to the wrapped buffer from a byte array.
+ /// Position is increased by the number of byte written
+ ///
+ /// Source byte array
+ /// Index from where start copying
+ /// Number of bytes to copy
+ public void Write(byte[] buffer, int offset, int count)
+ {
+ if (buffer == null)
+ {
+ throw new ArgumentNullException("buffer");
+ }
+ if (offset < 0)
+ {
+ throw new ArgumentOutOfRangeException("offset");
+ }
+ if ((count <= 0) || ((m_Position + count) > m_Length))
+ {
+ throw new ArgumentOutOfRangeException("count");
+ }
+ IntPtr dest = (IntPtr)(m_BufferPtr.ToInt64() + m_Position);
+ Marshal.Copy(buffer, offset, dest, count);
+ m_Position += (uint)count;
+ }
+ }
+
+ ///
+ /// Managed buffer that implements the INSSBuffer interface.
+ /// When passing this buffer to unmanaged code you must
+ /// take in account that it is not a safe operation because
+ /// managed heap could be exposed through the pointer returned by
+ /// INSSBuffer methods.
+ ///
+ internal class ManBuffer : INSSBuffer
+ {
+ private uint m_UsedLength;
+ private uint m_MaxLength;
+ private byte[] m_Buffer;
+ private GCHandle handle;
+
+ ///
+ /// Create a buffer with specified size
+ ///
+ /// Maximun size of buffer
+ public ManBuffer(uint size)
+ {
+ m_Buffer = new byte[size];
+ m_MaxLength = m_UsedLength = size;
+ handle = GCHandle.Alloc(m_Buffer, GCHandleType.Pinned);
+ }
+ ~ManBuffer()
+ {
+ handle.Free();
+ }
+
+ ///
+ /// How many bytes are currently used in the buffer.
+ /// Equivalent to INSSBuffer.GetLentgh and INSSBuffer.SetLength
+ ///
+ public uint UsedLength
+ {
+ get { return m_UsedLength; }
+ set
+ {
+ if (value <= m_MaxLength)
+ {
+ m_UsedLength = value;
+ }
+ else
+ {
+ throw new ArgumentException();
+ }
+ }
+ }
+
+ ///
+ /// Maximun buffer size. Equivalent to INSSBuffer.GetMaxLength
+ ///
+ public uint MaxLength
+ {
+ get { return m_MaxLength; }
+ }
+
+ ///
+ /// Internal byte array that contain buffer data.
+ ///
+ public byte[] Buffer
+ {
+ get { return m_Buffer; }
+ }
+
+ #region INSSBuffer Members
+
+ public void GetLength(out uint pdwLength)
+ {
+ pdwLength = m_UsedLength;
+ }
+
+ public void SetLength(uint dwLength)
+ {
+ UsedLength = dwLength;
+ }
+
+ public void GetMaxLength(out uint pdwLength)
+ {
+ pdwLength = m_MaxLength;
+ }
+
+ public void GetBuffer(out IntPtr ppdwBuffer)
+ {
+ ppdwBuffer = handle.AddrOfPinnedObject();
+ }
+
+ public void GetBufferAndLength(out IntPtr ppdwBuffer, out uint pdwLength)
+ {
+ ppdwBuffer = handle.AddrOfPinnedObject();
+ pdwLength = m_UsedLength;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile.cs
new file mode 100644
index 00000000..e620182b
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile.cs
@@ -0,0 +1,59 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BDB-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMProfile
+ {
+ void GetVersion([Out] out WMT_VERSION pdwVersion);
+ void GetName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref uint pcchName);
+ void SetName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszName);
+ void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszDescription,
+ [In, Out] ref uint pcchDescription);
+ void SetDescription([In, MarshalAs(UnmanagedType.LPWStr)] string pwszDescription);
+ void GetStreamCount([Out] out uint pcStreams);
+ void GetStream([In] uint dwStreamIndex, [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ void GetStreamByNumber([In] ushort wStreamNum, [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ void RemoveStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ void RemoveStreamByNumber([In] ushort wStreamNum);
+ void AddStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ void ReconfigStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ void CreateNewStream([In] ref Guid guidStreamType,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ void GetMutualExclusionCount([Out] out uint pcME);
+ void GetMutualExclusion([In] uint dwMEIndex,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMMutualExclusion ppME);
+ void RemoveMutualExclusion([In, MarshalAs(UnmanagedType.Interface)] IWMMutualExclusion pME);
+ void AddMutualExclusion([In, MarshalAs(UnmanagedType.Interface)] IWMMutualExclusion pME);
+ void CreateNewMutualExclusion([Out, MarshalAs(UnmanagedType.Interface)] out IWMMutualExclusion ppME);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile2.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile2.cs
new file mode 100644
index 00000000..5279afc4
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile2.cs
@@ -0,0 +1,62 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("07E72D33-D94E-4be7-8843-60AE5FF7E5F5")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMProfile2 : IWMProfile
+ {
+ //IWMProfile
+ new void GetVersion([Out] out WMT_VERSION pdwVersion);
+ new void GetName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref uint pcchName);
+ new void SetName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszName);
+ new void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszDescription,
+ [In, Out] ref uint pcchDescription);
+ new void SetDescription([In, MarshalAs(UnmanagedType.LPWStr)] string pwszDescription);
+ new void GetStreamCount([Out] out uint pcStreams);
+ new void GetStream([In] uint dwStreamIndex, [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ new void GetStreamByNumber([In] ushort wStreamNum, [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ new void RemoveStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ new void RemoveStreamByNumber([In] ushort wStreamNum);
+ new void AddStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ new void ReconfigStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ new void CreateNewStream([In] ref Guid guidStreamType,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ new void GetMutualExclusionCount([Out] out uint pcME);
+ new void GetMutualExclusion([In] uint dwMEIndex,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMMutualExclusion ppME);
+ new void RemoveMutualExclusion([In, MarshalAs(UnmanagedType.Interface)] IWMMutualExclusion pME);
+ new void AddMutualExclusion([In, MarshalAs(UnmanagedType.Interface)] IWMMutualExclusion pME);
+ new void CreateNewMutualExclusion([Out, MarshalAs(UnmanagedType.Interface)] out IWMMutualExclusion ppME);
+ //IWMProfile2
+ void GetProfileID([Out] out Guid pguidID);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile3.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile3.cs
new file mode 100644
index 00000000..67626a20
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfile3.cs
@@ -0,0 +1,76 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("00EF96CC-A461-4546-8BCD-C9A28F0E06F5")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMProfile3 : IWMProfile2
+ {
+ //IWMProfile
+ new void GetVersion([Out] out WMT_VERSION pdwVersion);
+ new void GetName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszName,
+ [In, Out] ref uint pcchName);
+ new void SetName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszName);
+ new void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszDescription,
+ [In, Out] ref uint pcchDescription);
+ new void SetDescription([In, MarshalAs(UnmanagedType.LPWStr)] string pwszDescription);
+ new void GetStreamCount([Out] out uint pcStreams);
+ new void GetStream([In] uint dwStreamIndex, [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ new void GetStreamByNumber([In] ushort wStreamNum, [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ new void RemoveStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ new void RemoveStreamByNumber([In] ushort wStreamNum);
+ new void AddStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ new void ReconfigStream([In, MarshalAs(UnmanagedType.Interface)] IWMStreamConfig pConfig);
+ new void CreateNewStream([In] ref Guid guidStreamType,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamConfig ppConfig);
+ new void GetMutualExclusionCount([Out] out uint pcME);
+ new void GetMutualExclusion([In] uint dwMEIndex,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMMutualExclusion ppME);
+ new void RemoveMutualExclusion([In, MarshalAs(UnmanagedType.Interface)] IWMMutualExclusion pME);
+ new void AddMutualExclusion([In, MarshalAs(UnmanagedType.Interface)] IWMMutualExclusion pME);
+ new void CreateNewMutualExclusion([Out, MarshalAs(UnmanagedType.Interface)] out IWMMutualExclusion ppME);
+ //IWMProfile2
+ new void GetProfileID([Out] out Guid pguidID);
+ //IWMProfile3
+ void GetStorageFormat([Out] out WMT_STORAGE_FORMAT pnStorageFormat);
+ void SetStorageFormat([In] WMT_STORAGE_FORMAT nStorageFormat);
+ void GetBandwidthSharingCount([Out] out uint pcBS);
+ void GetBandwidthSharing([In] uint dwBSIndex,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMBandwidthSharing ppBS);
+ void RemoveBandwidthSharing([In, MarshalAs(UnmanagedType.Interface)] IWMBandwidthSharing pBS);
+ void AddBandwidthSharing([In, MarshalAs(UnmanagedType.Interface)] IWMBandwidthSharing pBS);
+ void CreateNewBandwidthSharing([Out, MarshalAs(UnmanagedType.Interface)] out IWMBandwidthSharing ppBS);
+ void GetStreamPrioritization([Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamPrioritization ppSP);
+ void SetStreamPrioritization([In, MarshalAs(UnmanagedType.Interface)] IWMStreamPrioritization pSP);
+ void RemoveStreamPrioritization();
+ void CreateNewStreamPrioritization([Out, MarshalAs(UnmanagedType.Interface)] out IWMStreamPrioritization ppSP);
+ void GetExpectedPacketCount([In] ulong msDuration, [Out] out ulong pcPackets);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManager.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManager.cs
new file mode 100644
index 00000000..670279a4
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManager.cs
@@ -0,0 +1,49 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("d16679f2-6ca0-472d-8d31-2f5d55aee155")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMProfileManager
+ {
+ void CreateEmptyProfile([In] WMT_VERSION dwVersion,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ void LoadProfileByID([In] ref Guid guidProfile,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ void LoadProfileByData([In, MarshalAs(UnmanagedType.LPWStr)] string pwszProfile,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ void SaveProfile([In, MarshalAs(UnmanagedType.Interface)] IWMProfile pIWMProfile,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszProfile,
+ [In, Out] ref uint pdwLength);
+ void GetSystemProfileCount([Out] out uint pcProfiles);
+ void LoadSystemProfile([In] uint dwProfileIndex,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManager2.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManager2.cs
new file mode 100644
index 00000000..8dbebbe2
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManager2.cs
@@ -0,0 +1,53 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("7A924E51-73C1-494d-8019-23D37ED9B89A")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMProfileManager2 : IWMProfileManager
+ {
+ //IWMProfileManager
+ new void CreateEmptyProfile([In] WMT_VERSION dwVersion,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ new void LoadProfileByID([In] ref Guid guidProfile,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ new void LoadProfileByData([In, MarshalAs(UnmanagedType.LPWStr)] string pwszProfile,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ new void SaveProfile([In, MarshalAs(UnmanagedType.Interface)] IWMProfile pIWMProfile,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszProfile,
+ [In, Out] ref uint pdwLength);
+ new void GetSystemProfileCount([Out] out uint pcProfiles);
+ new void LoadSystemProfile([In] uint dwProfileIndex,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMProfile ppProfile);
+ //IWMProfileManager2
+ void GetSystemProfileVersion([Out] out WMT_VERSION pdwVersion);
+ void SetSystemProfileVersion([In] WMT_VERSION dwVersion);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManagerLanguage.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManagerLanguage.cs
new file mode 100644
index 00000000..483b44a0
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMProfileManagerLanguage.cs
@@ -0,0 +1,39 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("BA4DCC78-7EE0-4ab8-B27A-DBCE8BC51454")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMProfileManagerLanguage
+ {
+ void GetUserLanguageID([Out] out ushort wLangID);
+ void SetUserLanguageID([In] ushort wLangID);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig.cs
new file mode 100644
index 00000000..5070d1a4
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig.cs
@@ -0,0 +1,50 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BDC-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMStreamConfig
+ {
+ void GetStreamType([Out] out Guid pguidStreamType);
+ void GetStreamNumber([Out] out ushort pwStreamNum);
+ void SetStreamNumber([In] ushort wStreamNum);
+ void GetStreamName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszStreamName,
+ [In, Out] ref ushort pcchStreamName);
+ void SetStreamName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszStreamName);
+ void GetConnectionName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszInputName,
+ [In, Out] ref ushort pcchInputName);
+ void SetConnectionName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszInputName);
+ void GetBitrate([Out] out uint pdwBitrate);
+ void SetBitrate([In] uint pdwBitrate);
+ void GetBufferWindow([Out] out uint pmsBufferWindow);
+ void SetBufferWindow([In] uint msBufferWindow);
+ };
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig2.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig2.cs
new file mode 100644
index 00000000..4c0985d5
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig2.cs
@@ -0,0 +1,66 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("7688D8CB-FC0D-43BD-9459-5A8DEC200CFA")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMStreamConfig2 : IWMStreamConfig
+ {
+ //IWMStreamConfig
+ new void GetStreamType([Out] out Guid pguidStreamType);
+ new void GetStreamNumber([Out] out ushort pwStreamNum);
+ new void SetStreamNumber([In] ushort wStreamNum);
+ new void GetStreamName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszStreamName,
+ [In, Out] ref ushort pcchStreamName);
+ new void SetStreamName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszStreamName);
+ new void GetConnectionName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszInputName,
+ [In, Out] ref ushort pcchInputName);
+ new void SetConnectionName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszInputName);
+ new void GetBitrate([Out] out uint pdwBitrate);
+ new void SetBitrate([In] uint pdwBitrate);
+ new void GetBufferWindow([Out] out uint pmsBufferWindow);
+ new void SetBufferWindow([In] uint msBufferWindow);
+ //IWMStreamConfig2
+ void GetTransportType([Out] out WMT_TRANSPORT_TYPE pnTransportType);
+ void SetTransportType([In] WMT_TRANSPORT_TYPE nTransportType);
+ void AddDataUnitExtension([In] Guid guidExtensionSystemID,
+ [In] ushort cbExtensionDataSize,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] pbExtensionSystemInfo,
+ [In] uint cbExtensionSystemInfo);
+
+ void GetDataUnitExtensionCount([Out] out ushort pcDataUnitExtensions);
+ void GetDataUnitExtension([In] uint wDataUnitExtensionNumber,
+ [Out] out Guid pguidExtensionSystemID,
+ [Out] out ushort pcbExtensionDataSize,
+ /*[out, size_is( *pcbExtensionSystemInfo )]*/ IntPtr pbExtensionSystemInfo,
+ [In, Out] ref uint pcbExtensionSystemInfo);
+ void RemoveAllDataUnitExtensions();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig3.cs b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig3.cs
new file mode 100644
index 00000000..e763cf47
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ProfilesTypes/IWMStreamConfig3.cs
@@ -0,0 +1,70 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("CB164104-3AA9-45a7-9AC9-4DAEE131D6E1")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMStreamConfig3 : IWMStreamConfig2
+ {
+ //IWMStreamConfig
+ new void GetStreamType([Out] out Guid pguidStreamType);
+ new void GetStreamNumber([Out] out ushort pwStreamNum);
+ new void SetStreamNumber([In] ushort wStreamNum);
+ new void GetStreamName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszStreamName,
+ [In, Out] ref ushort pcchStreamName);
+ new void SetStreamName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszStreamName);
+ new void GetConnectionName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszInputName,
+ [In, Out] ref ushort pcchInputName);
+ new void SetConnectionName([In, MarshalAs(UnmanagedType.LPWStr)] string pwszInputName);
+ new void GetBitrate([Out] out uint pdwBitrate);
+ new void SetBitrate([In] uint pdwBitrate);
+ new void GetBufferWindow([Out] out uint pmsBufferWindow);
+ new void SetBufferWindow([In] uint msBufferWindow);
+ //IWMStreamConfig2
+ new void GetTransportType([Out] out WMT_TRANSPORT_TYPE pnTransportType);
+ new void SetTransportType([In] WMT_TRANSPORT_TYPE nTransportType);
+ new void AddDataUnitExtension([In] Guid guidExtensionSystemID,
+ [In] ushort cbExtensionDataSize,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] pbExtensionSystemInfo,
+ [In] uint cbExtensionSystemInfo);
+
+ new void GetDataUnitExtensionCount([Out] out ushort pcDataUnitExtensions);
+ new void GetDataUnitExtension([In] uint wDataUnitExtensionNumber,
+ [Out] out Guid pguidExtensionSystemID,
+ [Out] out ushort pcbExtensionDataSize,
+ /*[out, size_is( *pcbExtensionSystemInfo )]*/ IntPtr pbExtensionSystemInfo,
+ [In, Out] ref uint pcbExtensionSystemInfo);
+ new void RemoveAllDataUnitExtensions();
+ //IWMStreamConfig3
+ void GetLanguage([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszLanguageString,
+ [In, Out] ref ushort pcchLanguageStringLength);
+ void SetLanguage([In, MarshalAs(UnmanagedType.LPWStr)] string pwszLanguageString);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReader.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReader.cs
new file mode 100644
index 00000000..04827cb6
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReader.cs
@@ -0,0 +1,58 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ // wmsdkidl.h
+ [ComImport]
+ [Guid("96406BD6-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMReader
+ {
+ void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMReaderCallback pCallback,
+ [In] IntPtr pvContext);
+ void Close();
+ void GetOutputCount([Out] out uint pcOutputs);
+ void GetOutputProps([In] uint dwOutputNum,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMOutputMediaProps ppOutput);
+ void SetOutputProps([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMOutputMediaProps pOutput);
+ void GetOutputFormatCount([In] uint dwOutputNumber, [Out] out uint pcFormats);
+ void GetOutputFormat([In] uint dwOutputNumber,
+ [In] uint dwFormatNumber,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMOutputMediaProps ppProps);
+ void Start([In] ulong cnsStart,
+ [In] ulong cnsDuration,
+ [In] float fRate,
+ [In] IntPtr pvContext);
+ void Stop();
+ void Pause();
+ void Resume();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced.cs
new file mode 100644
index 00000000..ca7cbb3c
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced.cs
@@ -0,0 +1,59 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BEA-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMReaderAdvanced
+ {
+ void SetUserProvidedClock([In, MarshalAs(UnmanagedType.Bool)] bool fUserClock);
+ void GetUserProvidedClock([Out, MarshalAs(UnmanagedType.Bool)] out bool pfUserClock);
+ void DeliverTime([In] ulong cnsTime);
+ void SetManualStreamSelection([In, MarshalAs(UnmanagedType.Bool)] bool fSelection);
+ void GetManualStreamSelection([Out, MarshalAs(UnmanagedType.Bool)] out bool pfSelection);
+ void SetStreamsSelected([In] ushort cStreamCount,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ushort[] pwStreamNumbers,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] WMT_STREAM_SELECTION[] pSelections);
+ void GetStreamSelected([In] ushort wStreamNum, [Out] out WMT_STREAM_SELECTION pSelection);
+ void SetReceiveSelectionCallbacks([In, MarshalAs(UnmanagedType.Bool)] bool fGetCallbacks);
+ void GetReceiveSelectionCallbacks([Out, MarshalAs(UnmanagedType.Bool)] out bool pfGetCallbacks);
+ void SetReceiveStreamSamples([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fReceiveStreamSamples);
+ void GetReceiveStreamSamples([In] ushort wStreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfReceiveStreamSamples);
+ void SetAllocateForOutput([In] uint dwOutputNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ void GetAllocateForOutput([In] uint dwOutputNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ void SetAllocateForStream([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ void GetAllocateForStream([In] ushort dwSreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ void GetStatistics([In, Out] ref WM_READER_STATISTICS pStatistics);
+ void SetClientInfo([In] ref WM_READER_CLIENTINFO pClientInfo);
+ void GetMaxOutputSampleSize([In] uint dwOutput, [Out] out uint pcbMax);
+ void GetMaxStreamSampleSize([In] ushort wStream, [Out] out uint pcbMax);
+ void NotifyLateDelivery(ulong cnsLateness);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced2.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced2.cs
new file mode 100644
index 00000000..65627991
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced2.cs
@@ -0,0 +1,101 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("ae14a945-b90c-4d0d-9127-80d665f7d73e")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMReaderAdvanced2 : IWMReaderAdvanced
+ {
+ //IWMReaderAdvanced
+ new void SetUserProvidedClock([In, MarshalAs(UnmanagedType.Bool)] bool fUserClock);
+ new void GetUserProvidedClock([Out, MarshalAs(UnmanagedType.Bool)] out bool pfUserClock);
+ new void DeliverTime([In] ulong cnsTime);
+ new void SetManualStreamSelection([In, MarshalAs(UnmanagedType.Bool)] bool fSelection);
+ new void GetManualStreamSelection([Out, MarshalAs(UnmanagedType.Bool)] out bool pfSelection);
+ new void SetStreamsSelected([In] ushort cStreamCount,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ushort[] pwStreamNumbers,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] WMT_STREAM_SELECTION[] pSelections);
+ new void GetStreamSelected([In] ushort wStreamNum, [Out] out WMT_STREAM_SELECTION pSelection);
+ new void SetReceiveSelectionCallbacks([In, MarshalAs(UnmanagedType.Bool)] bool fGetCallbacks);
+ new void GetReceiveSelectionCallbacks([Out, MarshalAs(UnmanagedType.Bool)] out bool pfGetCallbacks);
+ new void SetReceiveStreamSamples([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fReceiveStreamSamples);
+ new void GetReceiveStreamSamples([In] ushort wStreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfReceiveStreamSamples);
+ new void SetAllocateForOutput([In] uint dwOutputNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ new void GetAllocateForOutput([In] uint dwOutputNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ new void SetAllocateForStream([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ new void GetAllocateForStream([In] ushort dwSreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ new void GetStatistics([In, Out] ref WM_READER_STATISTICS pStatistics);
+ new void SetClientInfo([In] ref WM_READER_CLIENTINFO pClientInfo);
+ new void GetMaxOutputSampleSize([In] uint dwOutput, [Out] out uint pcbMax);
+ new void GetMaxStreamSampleSize([In] ushort wStream, [Out] out uint pcbMax);
+ new void NotifyLateDelivery(ulong cnsLateness);
+ //IWMReaderAdvanced2
+ void SetPlayMode([In] WMT_PLAY_MODE Mode);
+ void GetPlayMode([Out] out WMT_PLAY_MODE pMode);
+ void GetBufferProgress([Out] out uint pdwPercent, [Out] out ulong pcnsBuffering);
+ void GetDownloadProgress([Out] out uint pdwPercent,
+ [Out] out ulong pqwBytesDownloaded,
+ [Out] out ulong pcnsDownload);
+ void GetSaveAsProgress([Out] out uint pdwPercent);
+ void SaveFileAs([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ void GetProtocolName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszProtocol,
+ [In, Out] ref uint pcchProtocol);
+ void StartAtMarker([In] ushort wMarkerIndex,
+ [In] ulong cnsDuration,
+ [In] float fRate,
+ [In] IntPtr pvContext);
+ void GetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ [In, Out] ref ushort pcbLength);
+
+ void SetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] pValue,
+ [In] ushort cbLength);
+ void Preroll([In] ulong cnsStart,
+ [In] ulong cnsDuration,
+ [In] float fRate);
+ void SetLogClientID([In, MarshalAs(UnmanagedType.Bool)] bool fLogClientID);
+ void GetLogClientID([Out, MarshalAs(UnmanagedType.Bool)] out bool pfLogClientID);
+ void StopBuffering();
+
+ //void OpenStream( [In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pStream,
+ // [In, MarshalAs(UnmanagedType.Interface)] IWMReaderCallback pCallback,
+ // [In] IntPtr pvContext );
+ // Yuval
+ void OpenStream([In, MarshalAs(UnmanagedType.Interface)] IStream pStream,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMReaderCallback pCallback,
+ [In] IntPtr pvContext);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced3.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced3.cs
new file mode 100644
index 00000000..55529146
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced3.cs
@@ -0,0 +1,108 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("5DC0674B-F04B-4a4e-9F2A-B1AFDE2C8100")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMReaderAdvanced3 : IWMReaderAdvanced2
+ {
+ //IWMReaderAdvanced
+ new void SetUserProvidedClock([In, MarshalAs(UnmanagedType.Bool)] bool fUserClock);
+ new void GetUserProvidedClock([Out, MarshalAs(UnmanagedType.Bool)] out bool pfUserClock);
+ new void DeliverTime([In] ulong cnsTime);
+ new void SetManualStreamSelection([In, MarshalAs(UnmanagedType.Bool)] bool fSelection);
+ new void GetManualStreamSelection([Out, MarshalAs(UnmanagedType.Bool)] out bool pfSelection);
+ new void SetStreamsSelected([In] ushort cStreamCount,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ushort[] pwStreamNumbers,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] WMT_STREAM_SELECTION[] pSelections);
+ new void GetStreamSelected([In] ushort wStreamNum, [Out] out WMT_STREAM_SELECTION pSelection);
+ new void SetReceiveSelectionCallbacks([In, MarshalAs(UnmanagedType.Bool)] bool fGetCallbacks);
+ new void GetReceiveSelectionCallbacks([Out, MarshalAs(UnmanagedType.Bool)] out bool pfGetCallbacks);
+ new void SetReceiveStreamSamples([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fReceiveStreamSamples);
+ new void GetReceiveStreamSamples([In] ushort wStreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfReceiveStreamSamples);
+ new void SetAllocateForOutput([In] uint dwOutputNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ new void GetAllocateForOutput([In] uint dwOutputNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ new void SetAllocateForStream([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ new void GetAllocateForStream([In] ushort dwSreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ new void GetStatistics([In, Out] ref WM_READER_STATISTICS pStatistics);
+ new void SetClientInfo([In] ref WM_READER_CLIENTINFO pClientInfo);
+ new void GetMaxOutputSampleSize([In] uint dwOutput, [Out] out uint pcbMax);
+ new void GetMaxStreamSampleSize([In] ushort wStream, [Out] out uint pcbMax);
+ new void NotifyLateDelivery(ulong cnsLateness);
+ //IWMReaderAdvanced2
+ new void SetPlayMode([In] WMT_PLAY_MODE Mode);
+ new void GetPlayMode([Out] out WMT_PLAY_MODE pMode);
+ new void GetBufferProgress([Out] out uint pdwPercent, [Out] out ulong pcnsBuffering);
+ new void GetDownloadProgress([Out] out uint pdwPercent,
+ [Out] out ulong pqwBytesDownloaded,
+ [Out] out ulong pcnsDownload);
+ new void GetSaveAsProgress([Out] out uint pdwPercent);
+ new void SaveFileAs([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ new void GetProtocolName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszProtocol,
+ [In, Out] ref uint pcchProtocol);
+ new void StartAtMarker([In] ushort wMarkerIndex,
+ [In] ulong cnsDuration,
+ [In] float fRate,
+ [In] IntPtr pvContext);
+ new void GetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ [In, Out] ref ushort pcbLength);
+ new void SetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] pValue,
+ [In] ushort cbLength);
+ new void Preroll([In] ulong cnsStart,
+ [In] ulong cnsDuration,
+ [In] float fRate);
+ new void SetLogClientID([In, MarshalAs(UnmanagedType.Bool)] bool fLogClientID);
+ new void GetLogClientID([Out, MarshalAs(UnmanagedType.Bool)] out bool pfLogClientID);
+ new void StopBuffering();
+ // new void OpenStream( [In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pStream,
+ // [In, MarshalAs(UnmanagedType.Interface)] IWMReaderCallback pCallback,
+ // [In] IntPtr pvContext );
+ // Yuval
+ new void OpenStream([In, MarshalAs(UnmanagedType.Interface)] IStream pStream,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMReaderCallback pCallback,
+ [In] IntPtr pvContext);
+
+ //IWMReaderAdvanced3
+ void StopNetStreaming();
+ void StartAtPosition([In] ushort wStreamNum,
+ [In] IntPtr pvOffsetStart,
+ [In] IntPtr pvDuration,
+ [In] WMT_OFFSET_FORMAT dwOffsetFormat,
+ [In] float fRate,
+ [In] IntPtr pvContext);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced4.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced4.cs
new file mode 100644
index 00000000..89308beb
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAdvanced4.cs
@@ -0,0 +1,125 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("945A76A2-12AE-4d48-BD3C-CD1D90399B85")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMReaderAdvanced4 : IWMReaderAdvanced3
+ {
+ //IWMReaderAdvanced
+ new void SetUserProvidedClock([In, MarshalAs(UnmanagedType.Bool)] bool fUserClock);
+ new void GetUserProvidedClock([Out, MarshalAs(UnmanagedType.Bool)] out bool pfUserClock);
+ new void DeliverTime([In] ulong cnsTime);
+ new void SetManualStreamSelection([In, MarshalAs(UnmanagedType.Bool)] bool fSelection);
+ new void GetManualStreamSelection([Out, MarshalAs(UnmanagedType.Bool)] out bool pfSelection);
+ new void SetStreamsSelected([In] ushort cStreamCount,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ushort[] pwStreamNumbers,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] WMT_STREAM_SELECTION[] pSelections);
+ new void GetStreamSelected([In] ushort wStreamNum, [Out] out WMT_STREAM_SELECTION pSelection);
+ new void SetReceiveSelectionCallbacks([In, MarshalAs(UnmanagedType.Bool)] bool fGetCallbacks);
+ new void GetReceiveSelectionCallbacks([Out, MarshalAs(UnmanagedType.Bool)] out bool pfGetCallbacks);
+ new void SetReceiveStreamSamples([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fReceiveStreamSamples);
+ new void GetReceiveStreamSamples([In] ushort wStreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfReceiveStreamSamples);
+ new void SetAllocateForOutput([In] uint dwOutputNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ new void GetAllocateForOutput([In] uint dwOutputNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ new void SetAllocateForStream([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Bool)] bool fAllocate);
+ new void GetAllocateForStream([In] ushort dwSreamNum, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfAllocate);
+ new void GetStatistics([In, Out] ref WM_READER_STATISTICS pStatistics);
+ new void SetClientInfo([In] ref WM_READER_CLIENTINFO pClientInfo);
+ new void GetMaxOutputSampleSize([In] uint dwOutput, [Out] out uint pcbMax);
+ new void GetMaxStreamSampleSize([In] ushort wStream, [Out] out uint pcbMax);
+ new void NotifyLateDelivery(ulong cnsLateness);
+ //IWMReaderAdvanced2
+ new void SetPlayMode([In] WMT_PLAY_MODE Mode);
+ new void GetPlayMode([Out] out WMT_PLAY_MODE pMode);
+ new void GetBufferProgress([Out] out uint pdwPercent, [Out] out ulong pcnsBuffering);
+ new void GetDownloadProgress([Out] out uint pdwPercent,
+ [Out] out ulong pqwBytesDownloaded,
+ [Out] out ulong pcnsDownload);
+ new void GetSaveAsProgress([Out] out uint pdwPercent);
+ new void SaveFileAs([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ new void GetProtocolName([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszProtocol,
+ [In, Out] ref uint pcchProtocol);
+ new void StartAtMarker([In] ushort wMarkerIndex,
+ [In] ulong cnsDuration,
+ [In] float fRate,
+ [In] IntPtr pvContext);
+ new void GetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ [In, Out] ref ushort pcbLength);
+ new void SetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] pValue,
+ [In] ushort cbLength);
+ new void Preroll([In] ulong cnsStart,
+ [In] ulong cnsDuration,
+ [In] float fRate);
+ new void SetLogClientID([In, MarshalAs(UnmanagedType.Bool)] bool fLogClientID);
+ new void GetLogClientID([Out, MarshalAs(UnmanagedType.Bool)] out bool pfLogClientID);
+ new void StopBuffering();
+ //new void OpenStream( [In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pStream,
+ // [In, MarshalAs(UnmanagedType.Interface)] IWMReaderCallback pCallback,
+ // [In] IntPtr pvContext );
+ // Yuval
+ new void OpenStream([In, MarshalAs(UnmanagedType.Interface)] IStream pStream,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMReaderCallback pCallback,
+ [In] IntPtr pvContext);
+
+ //IWMReaderAdvanced3
+ new void StopNetStreaming();
+ new void StartAtPosition([In] ushort wStreamNum,
+ [In] IntPtr pvOffsetStart,
+ [In] IntPtr pvDuration,
+ [In] WMT_OFFSET_FORMAT dwOffsetFormat,
+ [In] float fRate,
+ [In] IntPtr pvContext);
+ //IWMReaderAdvanced4
+ void GetLanguageCount([In] uint dwOutputNum,
+ [Out] out ushort pwLanguageCount);
+ void GetLanguage([In] uint dwOutputNum,
+ [In] ushort wLanguage,
+ [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszLanguageString,
+ [In, Out] ref ushort pcchLanguageStringLength);
+ void GetMaxSpeedFactor([Out] out double pdblFactor);
+ void IsUsingFastCache([Out, MarshalAs(UnmanagedType.Bool)] out bool pfUsingFastCache);
+ void AddLogParam([In, MarshalAs(UnmanagedType.LPWStr)] string wszNameSpace,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string wszName,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);
+ void SendLogParams();
+ void CanSaveFileAs([Out, MarshalAs(UnmanagedType.Bool)] out bool pfCanSave);
+ void CancelSaveFileAs();
+ void GetURL([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszURL,
+ [In, Out] ref uint pcchURL);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAllocatorEx.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAllocatorEx.cs
new file mode 100644
index 00000000..66e2d83d
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderAllocatorEx.cs
@@ -0,0 +1,52 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("9F762FA7-A22E-428d-93C9-AC82F3AAFE5A")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMReaderAllocatorEx
+ {
+ void AllocateForStreamEx([In] ushort wStreamNum,
+ [In] uint cbBuffer,
+ [Out] out INSSBuffer ppBuffer,
+ [In] uint dwFlags,
+ [In] ulong cnsSampleTime,
+ [In] ulong cnsSampleDuration,
+ [In] IntPtr pvContext);
+
+ void AllocateForOutputEx([In] uint dwOutputNum,
+ [In] uint cbBuffer,
+ [Out] out INSSBuffer ppBuffer,
+ [In] uint dwFlags,
+ [In] ulong cnsSampleTime,
+ [In] ulong cnsSampleDuration,
+ [In] IntPtr pvContext);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderCallback.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderCallback.cs
new file mode 100644
index 00000000..c18fa0f7
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMReaderCallback.cs
@@ -0,0 +1,51 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ // wmsdkidl.h
+ [ComImport]
+ [Guid("96406BD8-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMReaderCallback : IWMStatusCallback
+ {
+ //IWMStatusCallback
+ new void OnStatus([In] WMT_STATUS Status,
+ [In] IntPtr hr,
+ [In] WMT_ATTR_DATATYPE dwType,
+ [In] IntPtr pValue,
+ [In] IntPtr pvContext);
+ //IWMReaderCallback
+ void OnSample([In] uint dwOutputNum,
+ [In] ulong cnsSampleTime,
+ [In] ulong cnsSampleDuration,
+ [In] uint dwFlags,
+ [In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pSample,
+ [In] IntPtr pvContext);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMSyncReader.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMSyncReader.cs
new file mode 100644
index 00000000..ca13d4e2
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMSyncReader.cs
@@ -0,0 +1,83 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("9397F121-7705-4dc9-B049-98B698188414")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMSyncReader
+ {
+ void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ void Close();
+ void SetRange([In] ulong cnsStartTime, [In] long cnsDuration);
+ void SetRangeByFrame([In] ushort wStreamNum, [In] ulong qwFrameNumber, [In]long cFramesToRead);
+ void GetNextSample([In] ushort wStreamNum,
+ [Out] out INSSBuffer ppSample,
+ [Out] out ulong pcnsSampleTime,
+ [Out] out ulong pcnsDuration,
+ [Out] out uint pdwFlags,
+ [Out] out uint pdwOutputNum,
+ [Out] out ushort pwStreamNum);
+ void SetStreamsSelected([In] ushort cStreamCount,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ushort[] pwStreamNumbers,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] WMT_STREAM_SELECTION[] pSelections);
+ void GetStreamSelected([In]ushort wStreamNum,
+ [Out] out WMT_STREAM_SELECTION pSelection);
+ void SetReadStreamSamples([In] ushort wStreamNum,
+ [In, MarshalAs(UnmanagedType.Bool)] bool fCompressed);
+ void GetReadStreamSamples([In] ushort wStreamNum,
+ [Out, MarshalAs(UnmanagedType.Bool)] out bool pfCompressed);
+ void GetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ /*[out, size_is( *pcbLength )]*/ IntPtr pValue,
+ [In, Out] ref uint pcbLength);
+ void SetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] pValue,
+ [In] uint cbLength);
+ void GetOutputCount([Out] out uint pcOutputs);
+ void GetOutputProps([In] uint dwOutputNum, [Out, MarshalAs(UnmanagedType.Interface)] out IWMOutputMediaProps ppOutput);
+ void SetOutputProps([In] uint dwOutputNum, [In, MarshalAs(UnmanagedType.Interface)] IWMOutputMediaProps pOutput);
+ void GetOutputFormatCount([In] uint dwOutputNum, [Out] out uint pcFormats);
+ void GetOutputFormat([In] uint dwOutputNum,
+ [In] uint dwFormatNum,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMOutputMediaProps ppProps);
+ void GetOutputNumberForStream([In] ushort wStreamNum, [Out] out uint pdwOutputNum);
+ void GetStreamNumberForOutput([In] uint dwOutputNum, [Out] out ushort pwStreamNum);
+ void GetMaxOutputSampleSize([In] uint dwOutput, [Out] out uint pcbMax);
+ void GetMaxStreamSampleSize([In] ushort wStream, [Out] out uint pcbMax);
+ // void OpenStream( [In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pStream );
+ // Yuval
+ void OpenStream([In, MarshalAs(UnmanagedType.Interface)] IStream pStream);
+
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMSyncReader2.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMSyncReader2.cs
new file mode 100644
index 00000000..4c3bd761
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/IWMSyncReader2.cs
@@ -0,0 +1,96 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("faed3d21-1b6b-4af7-8cb6-3e189bbc187b")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMSyncReader2 : IWMSyncReader
+ {
+ //IWMSyncReader
+ new void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ new void Close();
+ new void SetRange([In] ulong cnsStartTime, [In] long cnsDuration);
+ new void SetRangeByFrame([In] ushort wStreamNum, [In] ulong qwFrameNumber, [In]long cFramesToRead);
+ new void GetNextSample([In] ushort wStreamNum,
+ [Out] out INSSBuffer ppSample,
+ [Out] out ulong pcnsSampleTime,
+ [Out] out ulong pcnsDuration,
+ [Out] out uint pdwFlags,
+ [Out] out uint pdwOutputNum,
+ [Out] out ushort pwStreamNum);
+ new void SetStreamsSelected([In] ushort cStreamCount,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] ushort[] pwStreamNumbers,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] WMT_STREAM_SELECTION[] pSelections);
+ new void GetStreamSelected([In]ushort wStreamNum,
+ [Out] out WMT_STREAM_SELECTION pSelection);
+ new void SetReadStreamSamples([In] ushort wStreamNum,
+ [In, MarshalAs(UnmanagedType.Bool)] bool fCompressed);
+ new void GetReadStreamSamples([In] ushort wStreamNum,
+ [Out, MarshalAs(UnmanagedType.Bool)] out bool pfCompressed);
+ new void GetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ /*[out, size_is( *pcbLength )]*/ IntPtr pValue,
+ [In, Out] ref uint pcbLength);
+ new void SetOutputSetting([In] uint dwOutputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] byte[] pValue,
+ [In] uint cbLength);
+ new void GetOutputCount([Out] out uint pcOutputs);
+ new void GetOutputProps([In] uint dwOutputNum, [Out, MarshalAs(UnmanagedType.Interface)] out IWMOutputMediaProps ppOutput);
+ new void SetOutputProps([In] uint dwOutputNum, [In, MarshalAs(UnmanagedType.Interface)] IWMOutputMediaProps pOutput);
+ new void GetOutputFormatCount([In] uint dwOutputNum, [Out] out uint pcFormats);
+ new void GetOutputFormat([In] uint dwOutputNum,
+ [In] uint dwFormatNum,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMOutputMediaProps ppProps);
+ new void GetOutputNumberForStream([In] ushort wStreamNum, [Out] out uint pdwOutputNum);
+ new void GetStreamNumberForOutput([In] uint dwOutputNum, [Out] out ushort pwStreamNum);
+ new void GetMaxOutputSampleSize([In] uint dwOutput, [Out] out uint pcbMax);
+ new void GetMaxStreamSampleSize([In] ushort wStream, [Out] out uint pcbMax);
+ //new void OpenStream( [In, MarshalAs(UnmanagedType.Interface)] UCOMIStream pStream );
+ // Yuval
+ new void OpenStream([In, MarshalAs(UnmanagedType.Interface)] IStream pStream);
+ //IWMSyncReader2
+ void SetRangeByTimecode([In] ushort wStreamNum,
+ [In] ref WMT_TIMECODE_EXTENSION_DATA pStart,
+ [In] ref WMT_TIMECODE_EXTENSION_DATA pEnd);
+
+ void SetRangeByFrameEx([In] ushort wStreamNum,
+ [In] ulong qwFrameNumber,
+ [In] long cFramesToRead,
+ [Out] out ulong pcnsStartTime);
+ void SetAllocateForOutput([In] uint dwOutputNum, [In, MarshalAs(UnmanagedType.Interface)] IWMReaderAllocatorEx pAllocator);
+ void GetAllocateForOutput([In] uint dwOutputNum, [Out, MarshalAs(UnmanagedType.Interface)] out IWMReaderAllocatorEx ppAllocator);
+ void SetAllocateForStream([In] ushort wStreamNum, [In, MarshalAs(UnmanagedType.Interface)] IWMReaderAllocatorEx pAllocator);
+ void GetAllocateForStream([In] ushort dwSreamNum, [Out, MarshalAs(UnmanagedType.Interface)] out IWMReaderAllocatorEx ppAllocator);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/WM_READER_CLIENTINFO.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/WM_READER_CLIENTINFO.cs
new file mode 100644
index 00000000..dd38fd2b
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/WM_READER_CLIENTINFO.cs
@@ -0,0 +1,49 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct WM_READER_CLIENTINFO
+ {
+ public uint cbSize;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string wszLang;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string wszBrowserUserAgent;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string wszBrowserWebPage;
+ ulong qwReserved;
+ public IntPtr pReserved;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string wszHostExe;
+ public ulong qwHostVersion;
+ [MarshalAs(UnmanagedType.LPWStr)]
+ public string wszPlayerUserAgent;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/ReaderTypes/WM_READER_STATISTICS.cs b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/WM_READER_STATISTICS.cs
new file mode 100644
index 00000000..8003e0f3
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/ReaderTypes/WM_READER_STATISTICS.cs
@@ -0,0 +1,41 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WM_READER_STATISTICS
+ {
+ public uint cbSize;
+ public uint dwBandwidth;
+ public uint cPacketsReceived;
+ public uint cPacketsRecovered;
+ public uint cPacketsLost;
+ public uint wQuality;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WMFFunctions.cs b/NAudio.WindowsMediaFormat/Interop/WMFFunctions.cs
new file mode 100644
index 00000000..2019a2ee
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WMFFunctions.cs
@@ -0,0 +1,502 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Helper class that define the Windows Media Format Functions and constants
+ ///
+ public static class WM
+ {
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateReader", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateReader(IntPtr pUnkReserved,
+ WMT_RIGHTS dwRights,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMReader ppReader);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateSyncReader", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateSyncReader(IntPtr pUnkCert,
+ WMT_RIGHTS dwRights,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMSyncReader ppSyncReader);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateProfileManager", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateProfileManager([Out, MarshalAs(UnmanagedType.Interface)] out IWMProfileManager ppProfileManager);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateEditor", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateEditor([Out, MarshalAs(UnmanagedType.Interface)] out IWMMetadataEditor ppEditor);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateIndexer", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateIndexer([Out, MarshalAs(UnmanagedType.Interface)] out IWMIndexer ppIndexer);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateWriter", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateWriter(IntPtr pUnkReserved, [Out, MarshalAs(UnmanagedType.Interface)] out IWMWriter ppWriter);
+
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateWriterFileSink", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateWriterFileSink([Out, MarshalAs(UnmanagedType.Interface)] out IWMWriterFileSink ppSink);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateWriterNetworkSink", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateWriterNetworkSink([Out, MarshalAs(UnmanagedType.Interface)] out IWMWriterNetworkSink ppSink);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCreateWriterPushSink", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCreateWriterPushSink([Out, MarshalAs(UnmanagedType.Interface)] out IWMWriterPushSink ppSink);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMIsAvailableOffline", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMIsAvailableOffline([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszLanguage,
+ [Out, MarshalAs(UnmanagedType.Bool)] out bool pfIsAvailableOffline);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMIsContentProtected", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMIsContentProtected([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFileName,
+ [Out, MarshalAs(UnmanagedType.Bool)] out bool pfIsProtected);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMValidateData", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMValidateData([In, MarshalAs(UnmanagedType.LPArray)] byte[] pbData,
+ [In, Out] ref uint pdwDataSize);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCheckURLExtension", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCheckURLExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL);
+
+ [DllImport("WMVCore.dll", EntryPoint = "WMCheckURLScheme", SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WMCheckURLScheme([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURLScheme);
+
+ ///
+ /// Wraps the WMWMCreateReader function
+ ///
+ /// Indicates the desired operation. See WMF SDK documentation
+ /// The reader object
+ public static IWMReader CreateReader(WMT_RIGHTS Rights)
+ {
+ IWMReader res = null;
+ Marshal.ThrowExceptionForHR(WMCreateReader(IntPtr.Zero, Rights, out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateSyncReader fucntion
+ ///
+ /// Indicates the desired operation. See WMF SDK documentation
+ /// The reader object
+ public static IWMSyncReader CreateSyncReader(WMT_RIGHTS Rights)
+ {
+ IWMSyncReader res = null;
+ Marshal.ThrowExceptionForHR(WMCreateSyncReader(IntPtr.Zero, Rights, out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateProfileManger function
+ ///
+ /// The profile manager object
+ public static IWMProfileManager CreateProfileManager()
+ {
+ IWMProfileManager res = null;
+ Marshal.ThrowExceptionForHR(WMCreateProfileManager(out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateEditor function
+ ///
+ /// The meta editor object
+ public static IWMMetadataEditor CreateEditor()
+ {
+ IWMMetadataEditor res = null;
+ Marshal.ThrowExceptionForHR(WMCreateEditor(out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateIndexer function
+ ///
+ /// The indexer object
+ public static IWMIndexer CreateIndexer()
+ {
+ IWMIndexer res = null;
+ Marshal.ThrowExceptionForHR(WMCreateIndexer(out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateWriter function
+ ///
+ /// The writer object
+ public static IWMWriter CreateWriter()
+ {
+ IWMWriter res = null;
+ Marshal.ThrowExceptionForHR(WMCreateWriter(IntPtr.Zero, out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateWriterFileSink function
+ ///
+ /// The file sink object
+ public static IWMWriterFileSink CreateWriterFileSink()
+ {
+ IWMWriterFileSink res = null;
+ Marshal.ThrowExceptionForHR(WMCreateWriterFileSink(out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateWriterNetworkSink function
+ ///
+ /// The network sink object
+ public static IWMWriterNetworkSink CreateWriterNetworkSink()
+ {
+ IWMWriterNetworkSink res = null;
+ Marshal.ThrowExceptionForHR(WMCreateWriterNetworkSink(out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMCreateWriterPushSink function
+ ///
+ /// The writer push sink object
+ public static IWMWriterPushSink CreateWriterPushSink()
+ {
+ IWMWriterPushSink res = null;
+ Marshal.ThrowExceptionForHR(WMCreateWriterPushSink(out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMIsAvailableOffline function
+ ///
+ /// URL to be checked
+ /// Wide-character null-terminated string containing the
+ /// RFC 1766-compliant language ID specifying which language is desired for playback.
+ /// See the WMF SDK for details.
+ /// True if URL can be played offline, False otherwise.
+ public static bool IsAvailableOffline(string URL, string Language)
+ {
+ bool res = false;
+ Marshal.ThrowExceptionForHR(WMIsAvailableOffline(URL, Language, out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMIsContentProtected function
+ ///
+ /// Name of the file to check
+ /// True if it is protected, False otherwise.
+ public static bool IsContentProtected(string FileName)
+ {
+ bool res = false;
+ Marshal.ThrowExceptionForHR(WMIsContentProtected(FileName, out res));
+ return res;
+ }
+
+ ///
+ /// Wraps the WMValidateData.
+ /// Raise a CComException if data don't represent a valid ASF content
+ ///
+ /// Buffer to check. The minimun buffer size is returned by
+ ///
+ /// must be the beggining of the ASF stream
+ public static void ValidateData(byte[] Data)
+ {
+ uint DataSize = (uint)Data.Length;
+ Marshal.ThrowExceptionForHR(WMValidateData(Data, ref DataSize));
+ }
+
+ ///
+ /// Minimum buffer size to pass to
+ ///
+ public static uint ValidateDataMinBuffSize
+ {
+ get
+ {
+ uint DataSize = 0;
+ Marshal.ThrowExceptionForHR(WMValidateData(null, ref DataSize));
+ return DataSize;
+ }
+ }
+
+ ///
+ /// Wraps the WMCheckURLExtension
+ ///
+ /// URL or file name to chekc
+ /// True if the specified fyle type can be opened by WMF objects. False otherwise
+ public static bool CheckURLExtension(string URL)
+ {
+ return WMCheckURLExtension(URL) == 0; //S_OK;
+ }
+
+ ///
+ /// Wraps the WMCheckURLScheme functions. Examines a network protocol
+ /// and compares it to an internal list of commonly used schemes.
+ ///
+ /// URL to check
+ /// True is it is a valid protocol scheme. False otherwise
+ public static bool CheckURLScheme(string URLScheme)
+ {
+ return WMCheckURLScheme(URLScheme) == 0;
+ }
+
+ private static IWMProfileManager m_ProfileManager = null;
+
+ ///
+ /// Static profile manager object. Use this property instead of calling
+ /// because creating and
+ /// realeasing profile managers can impact the performance.
+ ///
+ public static IWMProfileManager ProfileManager
+ {
+ get
+ {
+ //This is probably a bad idea. Multi threads using the same COM object usually causes problems.
+ if (m_ProfileManager == null)
+ {
+
+ m_ProfileManager = CreateProfileManager();
+ IWMProfileManager2 pm2 = (IWMProfileManager2)m_ProfileManager;
+ pm2.SetSystemProfileVersion(WMT_VERSION.WMT_VER_9_0);
+ }
+ return m_ProfileManager;
+ }
+ }
+
+ //Consts.
+ public const int NS_E_NO_MORE_SAMPLES = unchecked((int)0xC00D0BCF);
+ public const int ASF_E_NOTFOUND = unchecked((int)0xC00D07F0);
+ public const uint g_dwWMSpecialAttributes = 20;
+ public const string g_wszWMDuration = "Duration";
+ public const string g_wszWMBitrate = "Bitrate";
+ public const string g_wszWMSeekable = "Seekable";
+ public const string g_wszWMStridable = "Stridable";
+ public const string g_wszWMBroadcast = "Broadcast";
+ public const string g_wszWMProtected = "Is_Protected";
+ public const string g_wszWMTrusted = "Is_Trusted";
+ public const string g_wszWMSignature_Name = "Signature_Name";
+ public const string g_wszWMHasAudio = "HasAudio";
+ public const string g_wszWMHasImage = "HasImage";
+ public const string g_wszWMHasScript = "HasScript";
+ public const string g_wszWMHasVideo = "HasVideo";
+ public const string g_wszWMCurrentBitrate = "CurrentBitrate";
+ public const string g_wszWMOptimalBitrate = "OptimalBitrate";
+ public const string g_wszWMHasAttachedImages = "HasAttachedImages";
+ public const string g_wszWMSkipBackward = "Can_Skip_Backward";
+ public const string g_wszWMSkipForward = "Can_Skip_Forward";
+ public const string g_wszWMNumberOfFrames = "NumberOfFrames";
+ public const string g_wszWMFileSize = "FileSize";
+ public const string g_wszWMHasArbitraryDataStream = "HasArbitraryDataStream";
+ public const string g_wszWMHasFileTransferStream = "HasFileTransferStream";
+ public const string g_wszWMContainerFormat = "WM/ContainerFormat";
+ public const uint g_dwWMContentAttributes = 5;
+ public const string g_wszWMTitle = "Title";
+ public const string g_wszWMAuthor = "Author";
+ public const string g_wszWMDescription = "Description";
+ public const string g_wszWMRating = "Rating";
+ public const string g_wszWMCopyright = "Copyright";
+ public const string g_wszWMUse_DRM = "Use_DRM";
+ public const string g_wszWMDRM_Flags = "DRM_Flags";
+ public const string g_wszWMDRM_Level = "DRM_Level";
+ public const string g_wszWMUse_Advanced_DRM = "Use_Advanced_DRM";
+ public const string g_wszWMDRM_KeySeed = "DRM_KeySeed";
+ public const string g_wszWMDRM_KeyID = "DRM_KeyID";
+ public const string g_wszWMDRM_ContentID = "DRM_ContentID";
+ public const string g_wszWMDRM_IndividualizedVersion = "DRM_IndividualizedVersion";
+ public const string g_wszWMDRM_LicenseAcqURL = "DRM_LicenseAcqURL";
+ public const string g_wszWMDRM_V1LicenseAcqURL = "DRM_V1LicenseAcqURL";
+ public const string g_wszWMDRM_HeaderSignPrivKey = "DRM_HeaderSignPrivKey";
+ public const string g_wszWMDRM_LASignaturePrivKey = "DRM_LASignaturePrivKey";
+ public const string g_wszWMDRM_LASignatureCert = "DRM_LASignatureCert";
+ public const string g_wszWMDRM_LASignatureLicSrvCert = "DRM_LASignatureLicSrvCert";
+ public const string g_wszWMDRM_LASignatureRootCert = "DRM_LASignatureRootCert";
+ public const string g_wszWMAlbumTitle = "WM/AlbumTitle";
+ public const string g_wszWMTrack = "WM/Track";
+ public const string g_wszWMPromotionURL = "WM/PromotionURL";
+ public const string g_wszWMAlbumCoverURL = "WM/AlbumCoverURL";
+ public const string g_wszWMGenre = "WM/Genre";
+ public const string g_wszWMYear = "WM/Year";
+ public const string g_wszWMGenreID = "WM/GenreID";
+ public const string g_wszWMMCDI = "WM/MCDI";
+ public const string g_wszWMComposer = "WM/Composer";
+ public const string g_wszWMLyrics = "WM/Lyrics";
+ public const string g_wszWMTrackNumber = "WM/TrackNumber";
+ public const string g_wszWMToolName = "WM/ToolName";
+ public const string g_wszWMToolVersion = "WM/ToolVersion";
+ public const string g_wszWMIsVBR = "IsVBR";
+ public const string g_wszWMAlbumArtist = "WM/AlbumArtist";
+ public const string g_wszWMBannerImageType = "BannerImageType";
+ public const string g_wszWMBannerImageData = "BannerImageData";
+ public const string g_wszWMBannerImageURL = "BannerImageURL";
+ public const string g_wszWMCopyrightURL = "CopyrightURL";
+ public const string g_wszWMAspectRatioX = "AspectRatioX";
+ public const string g_wszWMAspectRatioY = "AspectRatioY";
+ public const string g_wszASFLeakyBucketPairs = "ASFLeakyBucketPairs";
+ public const uint g_dwWMNSCAttributes = 5;
+ public const string g_wszWMNSCName = "NSC_Name";
+ public const string g_wszWMNSCAddress = "NSC_Address";
+ public const string g_wszWMNSCPhone = "NSC_Phone";
+ public const string g_wszWMNSCEmail = "NSC_Email";
+ public const string g_wszWMNSCDescription = "NSC_Description";
+ public const string g_wszWMWriter = "WM/Writer";
+ public const string g_wszWMConductor = "WM/Conductor";
+ public const string g_wszWMProducer = "WM/Producer";
+ public const string g_wszWMDirector = "WM/Director";
+ public const string g_wszWMContentGroupDescription = "WM/ContentGroupDescription";
+ public const string g_wszWMSubTitle = "WM/SubTitle";
+ public const string g_wszWMPartOfSet = "WM/PartOfSet";
+ public const string g_wszWMProtectionType = "WM/ProtectionType";
+ public const string g_wszWMVideoHeight = "WM/VideoHeight";
+ public const string g_wszWMVideoWidth = "WM/VideoWidth";
+ public const string g_wszWMVideoFrameRate = "WM/VideoFrameRate";
+ public const string g_wszWMMediaClassPrimaryID = "WM/MediaClassPrimaryID";
+ public const string g_wszWMMediaClassSecondaryID = "WM/MediaClassSecondaryID";
+ public const string g_wszWMPeriod = "WM/Period";
+ public const string g_wszWMCategory = "WM/Category";
+ public const string g_wszWMPicture = "WM/Picture";
+ public const string g_wszWMLyrics_Synchronised = "WM/Lyrics_Synchronised";
+ public const string g_wszWMOriginalLyricist = "WM/OriginalLyricist";
+ public const string g_wszWMOriginalArtist = "WM/OriginalArtist";
+ public const string g_wszWMOriginalAlbumTitle = "WM/OriginalAlbumTitle";
+ public const string g_wszWMOriginalReleaseYear = "WM/OriginalReleaseYear";
+ public const string g_wszWMOriginalFilename = "WM/OriginalFilename";
+ public const string g_wszWMPublisher = "WM/Publisher";
+ public const string g_wszWMEncodedBy = "WM/EncodedBy";
+ public const string g_wszWMEncodingSettings = "WM/EncodingSettings";
+ public const string g_wszWMEncodingTime = "WM/EncodingTime";
+ public const string g_wszWMAuthorURL = "WM/AuthorURL";
+ public const string g_wszWMUserWebURL = "WM/UserWebURL";
+ public const string g_wszWMAudioFileURL = "WM/AudioFileURL";
+ public const string g_wszWMAudioSourceURL = "WM/AudioSourceURL";
+ public const string g_wszWMLanguage = "WM/Language";
+ public const string g_wszWMParentalRating = "WM/ParentalRating";
+ public const string g_wszWMBeatsPerMinute = "WM/BeatsPerMinute";
+ public const string g_wszWMInitialKey = "WM/InitialKey";
+ public const string g_wszWMMood = "WM/Mood";
+ public const string g_wszWMText = "WM/Text";
+ public const string g_wszWMDVDID = "WM/DVDID";
+ public const string g_wszWMWMContentID = "WM/WMContentID";
+ public const string g_wszWMWMCollectionID = "WM/WMCollectionID";
+ public const string g_wszWMWMCollectionGroupID = "WM/WMCollectionGroupID";
+ public const string g_wszWMUniqueFileIdentifier = "WM/UniqueFileIdentifier";
+ public const string g_wszWMModifiedBy = "WM/ModifiedBy";
+ public const string g_wszWMRadioStationName = "WM/RadioStationName";
+ public const string g_wszWMRadioStationOwner = "WM/RadioStationOwner";
+ public const string g_wszWMPlaylistDelay = "WM/PlaylistDelay";
+ public const string g_wszWMCodec = "WM/Codec";
+ public const string g_wszWMDRM = "WM/DRM";
+ public const string g_wszWMISRC = "WM/ISRC";
+ public const string g_wszWMProvider = "WM/Provider";
+ public const string g_wszWMProviderRating = "WM/ProviderRating";
+ public const string g_wszWMProviderStyle = "WM/ProviderStyle";
+ public const string g_wszWMContentDistributor = "WM/ContentDistributor";
+ public const string g_wszWMSubscriptionContentID = "WM/SubscriptionContentID";
+ public const string g_wszWMWMADRCPeakReference = "WM/WMADRCPeakReference";
+ public const string g_wszWMWMADRCPeakTarget = "WM/WMADRCPeakTarget";
+ public const string g_wszWMWMADRCAverageReference = "WM/WMADRCAverageReference";
+ public const string g_wszWMWMADRCAverageTarget = "WM/WMADRCAverageTarget";
+ public const string g_wszEarlyDataDelivery = "EarlyDataDelivery";
+ public const string g_wszJustInTimeDecode = "JustInTimeDecode";
+ public const string g_wszSingleOutputBuffer = "SingleOutputBuffer";
+ public const string g_wszSoftwareScaling = "SoftwareScaling";
+ public const string g_wszDeliverOnReceive = "DeliverOnReceive";
+ public const string g_wszScrambledAudio = "ScrambledAudio";
+ public const string g_wszDedicatedDeliveryThread = "DedicatedDeliveryThread";
+ public const string g_wszEnableDiscreteOutput = "EnableDiscreteOutput";
+ public const string g_wszSpeakerConfig = "SpeakerConfig";
+ public const string g_wszDynamicRangeControl = "DynamicRangeControl";
+ public const string g_wszAllowInterlacedOutput = "AllowInterlacedOutput";
+ public const string g_wszVideoSampleDurations = "VideoSampleDurations";
+ public const string g_wszStreamLanguage = "StreamLanguage";
+ public const string g_wszDeinterlaceMode = "DeinterlaceMode";
+ public const string g_wszInitialPatternForInverseTelecine = "InitialPatternForInverseTelecine";
+ public const string g_wszJPEGCompressionQuality = "JPEGCompressionQuality";
+ public const string g_wszWatermarkCLSID = "WatermarkCLSID";
+ public const string g_wszWatermarkConfig = "WatermarkConfig";
+ public const string g_wszInterlacedCoding = "InterlacedCoding";
+ public const string g_wszFixedFrameRate = "FixedFrameRate";
+ public const string g_wszOriginalSourceFormatTag = "_SOURCEFORMATTAG";
+ public const string g_wszOriginalWaveFormat = "_ORIGINALWAVEFORMAT";
+ public const string g_wszEDL = "_EDL";
+ public const string g_wszComplexity = "_COMPLEXITYEX";
+ public const string g_wszDecoderComplexityRequested = "_DECODERCOMPLEXITYPROFILE";
+ public const string g_wszReloadIndexOnSeek = "ReloadIndexOnSeek";
+ public const string g_wszStreamNumIndexObjects = "StreamNumIndexObjects";
+ public const string g_wszFailSeekOnError = "FailSeekOnError";
+ public const string g_wszPermitSeeksBeyondEndOfStream = "PermitSeeksBeyondEndOfStream";
+ public const string g_wszUsePacketAtSeekPoint = "UsePacketAtSeekPoint";
+ public const string g_wszSourceBufferTime = "SourceBufferTime";
+ public const string g_wszSourceMaxBytesAtOnce = "SourceMaxBytesAtOnce";
+ public const string g_wszVBREnabled = "_VBRENABLED";
+ public const string g_wszVBRQuality = "_VBRQUALITY";
+ public const string g_wszVBRBitrateMax = "_RMAX";
+ public const string g_wszVBRBufferWindowMax = "_BMAX";
+ public const string g_wszVBRPeak = "VBR Peak";
+ public const string g_wszBufferAverage = "Buffer Average";
+ public const string g_wszComplexityMax = "_COMPLEXITYEXMAX";
+ public const string g_wszComplexityOffline = "_COMPLEXITYEXOFFLINE";
+ public const string g_wszComplexityLive = "_COMPLEXITYEXLIVE";
+ public const string g_wszIsVBRSupported = "_ISVBRSUPPORTED";
+ public const string g_wszNumPasses = "_PASSESUSED";
+ public const string g_wszMusicSpeechClassMode = "MusicSpeechClassMode";
+ public const string g_wszMusicClassMode = "MusicClassMode";
+ public const string g_wszSpeechClassMode = "SpeechClassMode";
+ public const string g_wszMixedClassMode = "MixedClassMode";
+ public const string g_wszSpeechCaps = "SpeechFormatCap";
+ public const string g_wszPeakValue = "PeakValue";
+ public const string g_wszAverageLevel = "AverageLevel";
+ public const string g_wszFold6To2Channels3 = "Fold6To2Channels3";
+ public const string g_wszFoldToChannelsTemplate = "Fold%luTo%luChannels%lu";
+ public const string g_wszDeviceConformanceTemplate = "DeviceConformanceTemplate";
+ public const string g_wszEnableFrameInterpolation = "EnableFrameInterpolation";
+
+ }
+
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WMHeaderInfo.cs b/NAudio.WindowsMediaFormat/Interop/WMHeaderInfo.cs
new file mode 100644
index 00000000..bf7223be
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WMHeaderInfo.cs
@@ -0,0 +1,658 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Represent a marker in ASF streams
+ ///
+ public struct Marker
+ {
+ ///
+ /// Marker name
+ ///
+ public string Name;
+ ///
+ /// Marker time in 100-nanosecond units
+ ///
+ public ulong Time;
+ ///
+ /// Marker constructor
+ ///
+ /// Name of the marker
+ /// Time in 100-nanosecond units
+ public Marker(string name, ulong time)
+ {
+ this.Name = name;
+ this.Time = time;
+ }
+ }
+
+ ///
+ /// Represent a script in ASF streams
+ ///
+ public struct Script
+ {
+ ///
+ /// Type of the script. Its length must be less than 1024 characters.
+ ///
+ public string Type;
+ ///
+ /// The script command.
+ ///
+ public string Command;
+ ///
+ /// Script time in 100-nanosecond units
+ ///
+ public ulong Time;
+
+ ///
+ /// Scrip constructor
+ ///
+ /// Script type
+ /// Scrip Command
+ /// Time in 100-nanosecond units
+ public Script(string type, string command, ulong time)
+ {
+ this.Type = type;
+ this.Command = command;
+ this.Time = time;
+ }
+ }
+
+ ///
+ /// Represent the WMF attributes that can be present in a header of an ASF stream.
+ /// There are defined explicit convertion operator for every possible data type
+ /// represented with this structure.
+ ///
+ public struct WM_Attr
+ {
+ private WMT_ATTR_DATATYPE m_DataType;
+ private object m_Value;
+ private string m_Name;
+
+ ///
+ /// Initialize the WM_Attr streucture with proper values.
+ ///
+ /// Name of the attribute
+ /// WMT_ATTR_DATATYPE enum describing the type of the attribute.
+ /// The atrtibute value. This param is an obcjet and must match the
+ /// param type, ex. If type is WMT_ATTR_DATATYPE.WMT_TYPE_BOOL val param must be a valid bool
and so on.
+ public WM_Attr(string name, WMT_ATTR_DATATYPE type, object val)
+ {
+ m_Name = name;
+ this.m_DataType = type;
+ switch (type)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
+ m_Value = (byte[])val;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
+ m_Value = (bool)val;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
+ m_Value = (uint)val;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
+ m_Value = (Guid)val;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
+ m_Value = (ulong)val;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
+ m_Value = (string)val;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
+ m_Value = (ushort)val;
+ break;
+ default:
+ throw new ArgumentException("Invalid data type", "type");
+ }
+ }
+
+ ///
+ /// ToString is overrided to provide the string representation in "name=value" format.
+ ///
+ /// String represantation of this struct in "name=value" format.
+ public override string ToString()
+ {
+ return string.Format("{0} = {1}", m_Name, m_Value);
+ }
+
+ ///
+ /// Name of the attribute
+ ///
+ public string Name
+ {
+ get { return m_Name; }
+ }
+
+ ///
+ /// Data type of the attribute
+ ///
+ public WMT_ATTR_DATATYPE DataType
+ {
+ get { return m_DataType; }
+ }
+
+ ///
+ /// Read/Write object representing the value of the attribute.
+ /// When writing this property the object must match to the DataType
+ ///
+ public object Value
+ {
+ get
+ {
+ return m_Value;
+ }
+ set
+ {
+ switch (m_DataType)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
+ m_Value = (bool)value;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
+ m_Value = (uint)value;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
+ m_Value = (ushort)value;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
+ m_Value = (ulong)value;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
+ m_Value = (Guid)value;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
+ m_Value = (string)value;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
+ // Try Binary values as byte array only
+ m_Value = (byte[])value;
+ break;
+ };
+ }
+ }
+
+ #region Type convertion operators
+
+ public static explicit operator string(WM_Attr attr)
+ {
+ if (attr.m_DataType == WMT_ATTR_DATATYPE.WMT_TYPE_STRING)
+ {
+ return (string)attr.m_Value;
+ }
+ else
+ {
+ throw new InvalidCastException();
+ }
+ }
+
+ public static explicit operator bool(WM_Attr attr)
+ {
+ if (attr.m_DataType == WMT_ATTR_DATATYPE.WMT_TYPE_BOOL)
+ {
+ return (bool)attr.m_Value;
+ }
+ else
+ {
+ throw new InvalidCastException();
+ }
+ }
+
+ public static explicit operator Guid(WM_Attr attr)
+ {
+ if (attr.m_DataType == WMT_ATTR_DATATYPE.WMT_TYPE_GUID)
+ {
+ return (Guid)attr.m_Value;
+ }
+ else
+ {
+ throw new InvalidCastException();
+ }
+ }
+
+ public static explicit operator byte[](WM_Attr attr)
+ {
+ if (attr.m_DataType == WMT_ATTR_DATATYPE.WMT_TYPE_BINARY)
+ {
+ return (byte[])attr.m_Value;
+ }
+ else
+ {
+ throw new InvalidCastException();
+ }
+ }
+
+ public static explicit operator ulong(WM_Attr attr)
+ {
+ switch (attr.m_DataType)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
+ case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
+ case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
+ return (ulong)attr.m_Value;
+ default:
+ throw new InvalidCastException();
+ }
+ }
+
+ public static explicit operator long(WM_Attr attr)
+ {
+ return (long)(ulong)attr;
+ }
+
+ public static explicit operator int(WM_Attr attr)
+ {
+ return (int)(ulong)attr;
+ }
+
+ public static explicit operator uint(WM_Attr attr)
+ {
+ return (uint)(ulong)attr;
+ }
+
+ public static explicit operator ushort(WM_Attr attr)
+ {
+ return (ushort)(ulong)attr;
+ }
+ #endregion
+ }
+
+ ///
+ /// Helper class to encapsulate IWMHeaderInfo interface
+ ///
+ public class WMHeaderInfo
+ {
+ private IWMHeaderInfo m_HeaderInfo;
+
+ ///
+ /// WMHeaderInfo constructor
+ ///
+ /// IWMHeaderInfo to wrap
+ public WMHeaderInfo(IWMHeaderInfo headinfo)
+ {
+ m_HeaderInfo = headinfo;
+ }
+
+ ///
+ /// Internal IWMHeaderInfo
+ ///
+ public IWMHeaderInfo HeaderInfo
+ {
+ get { return m_HeaderInfo; }
+ }
+
+ ///
+ /// Add a marher to the header info. Wraps IWMHeaderInfo.AddMarker
+ ///
+ /// Marker to add.
+ public void AddMarker(Marker m)
+ {
+ m_HeaderInfo.AddMarker(m.Name, m.Time);
+ }
+
+ ///
+ /// Get a marker. Wraps IWMHeaderInfo.GetMarker
+ ///
+ /// Index of the desired marker
+ /// The desired marker.
+ public Marker GetMarker(int index)
+ {
+ ulong time;
+ ushort namelen = 0;
+ StringBuilder name;
+ m_HeaderInfo.GetMarker((ushort)index, null, ref namelen, out time);
+ name = new StringBuilder(namelen);
+ m_HeaderInfo.GetMarker((ushort)index, name, ref namelen, out time);
+ return new Marker(name.ToString(), time);
+ }
+
+ ///
+ /// Remove a marker. Wraps IWMHeaderInfo.RemoveMarker
+ ///
+ /// Index of the marker to remove
+ public void RemoveMarker(int index)
+ {
+ m_HeaderInfo.RemoveMarker((ushort)index);
+ }
+
+ ///
+ /// Add a scrip. Wraps IWMHeaderInfo.AddScript
+ ///
+ /// Scrip to add.
+ public void AddScript(Script s)
+ {
+ m_HeaderInfo.AddScript(s.Type, s.Command, s.Time);
+ }
+
+ ///
+ /// Get a script from the header info. Wraps IWMHeaderInfo.GetScript
+ ///
+ /// Index of desired script
+ /// Desired script.
+ public Script GetScript(int index)
+ {
+ ushort commandlen = 0, typelen = 0;
+ ulong time;
+ StringBuilder command, type;
+ m_HeaderInfo.GetScript((ushort)index, null, ref typelen, null, ref commandlen, out time);
+ command = new StringBuilder(commandlen);
+ type = new StringBuilder(typelen);
+ m_HeaderInfo.GetScript((ushort)index, type, ref typelen, command, ref commandlen, out time);
+ return new Script(type.ToString(), command.ToString(), time);
+ }
+
+ ///
+ /// Remove a script. Wraps IWMHeaderInfo.RemoveScript
+ ///
+ /// Index of script to remove
+ public void RemoveScript(int index)
+ {
+ m_HeaderInfo.RemoveScript((ushort)index);
+ }
+
+ ///
+ /// Number of scripts in header info object. Wraps IWMHeaderInfo.GetScriptCount
+ ///
+ public int ScriptCount
+ {
+ get
+ {
+ ushort res;
+ m_HeaderInfo.GetScriptCount(out res);
+ return res;
+ }
+ }
+
+ ///
+ /// Number of markers in the header info object. Wraps IWMHeaderInfo.GetMarkerCount
+ ///
+ public int MarkerCount
+ {
+ get
+ {
+ ushort res;
+ m_HeaderInfo.GetMarkerCount(out res);
+ return res;
+ }
+ }
+
+ ///
+ /// Number of markers in the header info object for the specified stream. Wraps IWMHeaderInfo.GetAttributeCount
+ ///
+ /// Stream number. Zero means file level attributes
+ /// Number of attributes
+ public int AttributeCount(int StreamNumber)
+ {
+ ushort res;
+ m_HeaderInfo.GetAttributeCount((ushort)StreamNumber, out res);
+ return res;
+ }
+
+ ///
+ /// File level attribute count.
+ ///
+ /// File level attribute count
+ public int AttributeCount()
+ {
+ return AttributeCount(0);
+ }
+
+ ///
+ /// Get the attribute by index of specific stream. Wraps Number of markers in the header info object. Wraps IWMHeaderInfo.GetAttibuteByIndex
+ ///
+ /// Stream number. Zero return a file level atrtibute
+ /// Attribute index
+ /// Desired attribute
+ public WM_Attr GetAttribute(int StreamNumber, int index)
+ {
+ WMT_ATTR_DATATYPE type;
+ StringBuilder name;
+ object obj;
+ ushort stream = (ushort)StreamNumber;
+ ushort namelen = 0;
+ ushort datalen = 0;
+ m_HeaderInfo.GetAttributeByIndex((ushort)index, ref stream, null, ref namelen, out type, IntPtr.Zero, ref datalen);
+ name = new StringBuilder(namelen);
+ switch (type)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
+ case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
+ obj = (uint)0;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
+ obj = Guid.NewGuid();
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
+ obj = (ulong)0;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
+ obj = (ushort)0;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
+ obj = new byte[datalen];
+ break;
+ default:
+ throw new InvalidOperationException(string.Format("Not supported data type: {0}", type.ToString()));
+ }
+ GCHandle h = GCHandle.Alloc(obj, GCHandleType.Pinned);
+ try
+ {
+ IntPtr ptr = h.AddrOfPinnedObject();
+ m_HeaderInfo.GetAttributeByIndex((ushort)index, ref stream, name, ref namelen, out type, ptr, ref datalen);
+ switch (type)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
+ obj = Marshal.PtrToStringUni(ptr);
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
+ obj = ((uint)obj != 0);
+ break;
+ }
+ }
+ finally
+ {
+ h.Free();
+ }
+ return new WM_Attr(name.ToString(), type, obj);
+ }
+
+ ///
+ /// Retrun the file level attribute by index.
+ ///
+ /// Index of desired attribute
+ ///
+ public WM_Attr GetAttribute(int index)
+ {
+ return GetAttribute(0, index);
+ }
+
+ ///
+ /// Get the header attribute by name and by stream number. Wraps IWMHeaderInfo.GetAttributeByName
+ ///
+ /// Stream numer. Zero means file level attributes
+ /// Nma of the desired attribute
+ /// Desired attribute
+ public WM_Attr GetAttribute(int StreamNumber, string name)
+ {
+ ushort stream = (ushort)StreamNumber;
+ ushort datalen = 0;
+ object obj;
+ WMT_ATTR_DATATYPE type;
+
+ m_HeaderInfo.GetAttributeByName(ref stream, name, out type, IntPtr.Zero, ref datalen);
+ switch (type)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
+ case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
+ obj = (uint)0;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
+ obj = Guid.NewGuid();
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
+ obj = (ulong)0;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
+ obj = (ushort)0;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
+ obj = new byte[datalen];
+ break;
+ default:
+ throw new InvalidOperationException(string.Format("Not supported data type: {0}", type.ToString()));
+ }
+ GCHandle h = GCHandle.Alloc(obj, GCHandleType.Pinned);
+ try
+ {
+ IntPtr ptr = h.AddrOfPinnedObject();
+ m_HeaderInfo.GetAttributeByName(ref stream, name, out type, ptr, ref datalen);
+ switch (type)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
+ obj = Marshal.PtrToStringUni(ptr);
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
+ obj = ((uint)obj != 0);
+ break;
+ }
+ }
+ finally
+ {
+ h.Free();
+ }
+ return new WM_Attr(name, type, obj);
+ }
+
+ ///
+ /// Return a file level attribute by name.
+ ///
+ /// Name of desired attribute
+ /// Desired attribute
+ public WM_Attr GetAttribute(string name)
+ {
+ return GetAttribute(0, name);
+ }
+
+ ///
+ /// Set an attribute specifying a stream number. Wraps IWMHeaderInfo.SetAttribute
+ ///
+ /// Stream number. Zero for file level attributes.
+ /// Attribute to set.
+ public void SetAttribute(int StreamNumber, WM_Attr attr)
+ {
+ object obj;
+ ushort size;
+ switch (attr.DataType)
+ {
+ case WMT_ATTR_DATATYPE.WMT_TYPE_STRING:
+ byte[] arr = Encoding.Unicode.GetBytes((string)attr.Value + (char)0);
+ obj = arr;
+ size = (ushort)arr.Length;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BOOL:
+ obj = (uint)((bool)attr ? 1 : 0);
+ size = 4;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_BINARY:
+ obj = (byte[])attr.Value;
+ size = (ushort)((byte[])obj).Length;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_DWORD:
+ obj = (uint)attr;
+ size = 4;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_QWORD:
+ obj = (ulong)attr;
+ size = 8;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_WORD:
+ obj = (ushort)attr;
+ size = 2;
+ break;
+ case WMT_ATTR_DATATYPE.WMT_TYPE_GUID:
+ obj = (Guid)attr;
+ size = (ushort)Marshal.SizeOf(typeof(Guid));
+ break;
+ default:
+ throw new ArgumentException("Invalid data type", "attr");
+ }
+ GCHandle h = GCHandle.Alloc(obj, GCHandleType.Pinned);
+ try
+ {
+ m_HeaderInfo.SetAttribute((ushort)StreamNumber, attr.Name, attr.DataType, h.AddrOfPinnedObject(), size);
+ }
+ finally
+ {
+ h.Free();
+ }
+ }
+
+ ///
+ /// Set file level attributes
+ ///
+ /// Attribute to set
+ public void SetAttribute(WM_Attr attr)
+ {
+ SetAttribute(0, attr);
+ }
+
+ ///
+ /// Read only. File level attributes indexed by integer index.
+ ///
+ [System.Runtime.CompilerServices.IndexerName("Attributes")]
+ public WM_Attr this[int index]
+ {
+ get
+ {
+ return GetAttribute(index);
+ }
+ }
+
+ ///
+ /// Read/Write. File level attributes indexed by name.
+ ///
+ [System.Runtime.CompilerServices.IndexerName("Attributes")]
+ public WM_Attr this[string name]
+ {
+ get
+ {
+ return GetAttribute(name);
+ }
+ set
+ {
+ SetAttribute(value);
+ }
+ }
+ }
+
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WMStreamConfig.cs b/NAudio.WindowsMediaFormat/Interop/WMStreamConfig.cs
new file mode 100644
index 00000000..b2d0b2e1
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WMStreamConfig.cs
@@ -0,0 +1,192 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Helper to encapsulate IWMStreamConfig.
+ ///
+ public class WMStreamConfig
+ {
+ private readonly IWMStreamConfig streamConfig;
+
+ ///
+ /// WMStreamConfig constructor
+ ///
+ /// IWMStreamConfig to wrap
+ public WMStreamConfig(IWMStreamConfig config)
+ {
+ streamConfig = config;
+ }
+
+ ///
+ /// Gets the waveformat of this stream
+ ///
+ /// A waveformat (or null if this is not an audio stream)
+ public WaveFormat GetWaveFormat()
+ {
+ var props = (IWMMediaProps) streamConfig;
+ int size = Math.Max(512, Marshal.SizeOf(typeof (WM_MEDIA_TYPE)) + Marshal.SizeOf(typeof (WaveFormat)));
+ IntPtr buffer = Marshal.AllocCoTaskMem(size);
+ try
+ {
+ props.GetMediaType(buffer, ref size);
+ var mt = (WM_MEDIA_TYPE)Marshal.PtrToStructure(buffer, typeof(WM_MEDIA_TYPE));
+ if ((mt.majortype == MediaTypes.WMMEDIATYPE_Audio) &&
+ // n.b. subtype may not be PCM, but some variation of WM Audio
+ (mt.formattype == MediaTypes.WMFORMAT_WaveFormatEx))
+ {
+ var fmt = new WaveFormatExtensible(44100, 16, 2);
+ Marshal.PtrToStructure(mt.pbFormat, fmt);
+ return fmt;
+ }
+ return null;
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(buffer);
+ }
+ }
+
+
+ ///
+ /// Wrapped IWMStreamConfig object
+ ///
+ public IWMStreamConfig StreamConfig
+ {
+ get { return streamConfig; }
+ }
+
+ ///
+ /// Read/Write. Bitrate of the stream. Wraps IWMStreamConfig.GetBitrate and WMStreamConfig.SetBitrate
+ ///
+ public uint Bitrate
+ {
+ get
+ {
+ uint res;
+ streamConfig.GetBitrate(out res);
+ return res;
+ }
+ set
+ {
+ streamConfig.SetBitrate(value);
+ }
+ }
+
+ ///
+ /// Get/Set the buffer window of the stream. Wraps IWMStreamConfig.GetBufferWindow and IWMStreamConfig.SetBufferWindow
+ ///
+ public uint BufferWindow
+ {
+ get
+ {
+ uint res;
+ streamConfig.GetBufferWindow(out res);
+ return res;
+ }
+ set
+ {
+ streamConfig.SetBufferWindow(value);
+ }
+ }
+
+ ///
+ /// Get/Set commention name. Wraps IWMStreamConfig.GetConnectionName and IWMStreamConfig.SetConnectionName
+ ///
+ public string ConnectionName
+ {
+ get
+ {
+ StringBuilder name;
+ ushort namelen = 0;
+ streamConfig.GetConnectionName(null, ref namelen);
+ name = new StringBuilder(namelen);
+ streamConfig.GetConnectionName(name, ref namelen);
+ return name.ToString();
+ }
+ set
+ {
+ streamConfig.SetConnectionName(value);
+ }
+ }
+
+ ///
+ /// Get/Set stream name. Wraps IWMStreamConfig.GetStreamName and IWMStreamConfig.SetStreamName
+ ///
+ public string StreamName
+ {
+ get
+ {
+ StringBuilder name;
+ ushort namelen = 0;
+ streamConfig.GetStreamName(null, ref namelen);
+ name = new StringBuilder(namelen);
+ streamConfig.GetStreamName(name, ref namelen);
+ return name.ToString();
+ }
+ set
+ {
+ streamConfig.SetStreamName(value);
+ }
+ }
+
+ ///
+ /// Get/Set stream number. Wraps IWMStreamConfig.GetStreamNumber and IWMStreamConfig.SetStreamNumber
+ ///
+ public ushort StreamNumber
+ {
+ get
+ {
+ ushort res;
+ streamConfig.GetStreamNumber(out res);
+ return res;
+ }
+ set
+ {
+ streamConfig.SetStreamNumber(value);
+ }
+ }
+
+ ///
+ /// Get the stream type (GUID that correspons of WM_MEDIA_TYPE.majortype
+ /// Wraps IWMStreamConfig.GetStreamType
+ ///
+ public Guid StreamType
+ {
+ get
+ {
+ Guid res;
+ streamConfig.GetStreamType(out res);
+ return res;
+ }
+ }
+ }
+
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WmProfile.cs b/NAudio.WindowsMediaFormat/Interop/WmProfile.cs
new file mode 100644
index 00000000..4e671fd7
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WmProfile.cs
@@ -0,0 +1,167 @@
+// Widows Media Format Utils classes
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+// adapted for NAudio by Mark Heath
+
+using System;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Helper class to encapsulate IWMProfile interface
+ ///
+ public class WMProfile
+ {
+ private IWMProfile m_Profile;
+
+ ///
+ /// WMProfile constructor
+ ///
+ /// Profile object to wrap
+ public WMProfile(IWMProfile profile)
+ {
+ m_Profile = profile;
+ }
+
+ ///
+ /// Wraps IWMProfile.GetStream
+ ///
+ /// Index of desired stream
+ /// IWMStreamConfig
+ public IWMStreamConfig GetStream(int index)
+ {
+ IWMStreamConfig res;
+ m_Profile.GetStream((uint)index, out res);
+ return res;
+ }
+
+ ///
+ /// Wraps IWMProfile.GetStreamByNumber
+ ///
+ /// Stream number
+ /// IWMStreamConfig
+ public IWMStreamConfig GetStreamByNumber(int number)
+ {
+ IWMStreamConfig res;
+ m_Profile.GetStreamByNumber((ushort)number, out res);
+ return res;
+ }
+
+ ///
+ /// Wraps IWMProfile.RemoveStream
+ ///
+ /// IWMStreamConfig stream to remove
+ public void RemoveStream(IWMStreamConfig strconfig)
+ {
+ m_Profile.RemoveStream(strconfig);
+ }
+
+ ///
+ /// Wraps IWMProfile.RemoveStreamByNumber
+ ///
+ /// Stream number to remove
+ public void RemoveStreamByNumber(int number)
+ {
+ m_Profile.RemoveStreamByNumber((ushort)number);
+ }
+
+ ///
+ /// Wraps IWMProfile.ReconfigStream
+ ///
+ /// IWMStreamConfig stream to reconfig
+ public void ReconfigStream(IWMStreamConfig strconfig)
+ {
+ m_Profile.ReconfigStream(strconfig);
+ }
+
+ ///
+ /// Wrapped IWMProfile object
+ ///
+ public IWMProfile Profile
+ {
+ get { return m_Profile; }
+ }
+
+ ///
+ /// Profile name. Wraps IWMProfile.GetName
+ ///
+ public string Name
+ {
+ get
+ {
+ uint len = 0;
+ StringBuilder s;
+ m_Profile.GetName(null, ref len);
+ s = new StringBuilder((int)len);
+ m_Profile.GetName(s, ref len);
+ return s.ToString();
+ }
+ set
+ {
+ m_Profile.SetName(value);
+ }
+ }
+
+ ///
+ /// Profile description. Wraps IWMProfile.GetDescription
+ ///
+ public string Description
+ {
+ get
+ {
+ uint len = 0;
+ StringBuilder s;
+ m_Profile.GetDescription(null, ref len);
+ s = new StringBuilder((int)len);
+ m_Profile.GetName(s, ref len);
+ return s.ToString();
+ }
+ set
+ {
+ m_Profile.SetDescription(value);
+ }
+ }
+
+ ///
+ /// String in XML representing the profile. Wraps IProfileManager.SaveProfile
+ ///
+ public string ProfileData
+ {
+ get
+ {
+ uint len = 0;
+ StringBuilder s;
+ WM.ProfileManager.SaveProfile(m_Profile, null, ref len);
+ s = new StringBuilder((int)len);
+ WM.ProfileManager.SaveProfile(m_Profile, s, ref len);
+ return s.ToString();
+ }
+ }
+
+ ///
+ /// Number of streams in the profile. Wraps IWMProfile.GetStreamCount
+ ///
+ public uint StreamCount
+ {
+ get
+ {
+ uint res;
+ m_Profile.GetStreamCount(out res);
+ return res;
+ }
+ }
+
+ }
+}
+
diff --git a/NAudio.WindowsMediaFormat/Interop/WmaStream.cs b/NAudio.WindowsMediaFormat/Interop/WmaStream.cs
new file mode 100644
index 00000000..6c5b0fd0
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WmaStream.cs
@@ -0,0 +1,627 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Stream that provides uncompressed audio data from any file that
+ /// can be read using the WMF (WMA, WMV, MP3, MPE, ASF, etc)
+ ///
+ public class WmaStream : Stream
+ {
+ ///
+ /// Create WmaStream with specific format for for uncompressed audio data.
+ ///
+ /// Name of asf file
+ /// WaveFormat that define the desired audio data format
+ public WmaStream(string FileName, WaveFormat OutputFormat)
+ {
+ m_reader = WM.CreateSyncReader(WMT_RIGHTS.WMT_RIGHT_NO_DRM);
+ try
+ {
+ m_reader.Open(FileName);
+ Init(OutputFormat);
+ }
+ catch
+ {
+ try
+ {
+ m_reader.Close();
+ }
+ catch
+ {
+ }
+ m_reader = null;
+ throw;
+ }
+ }
+
+ ///
+ /// Create WmaStream. The first PCM available for audio outputs will be used as output format.
+ /// Output format can be checked in property.
+ ///
+ /// Name of asf file
+ public WmaStream(string FileName)
+ : this(FileName, null)
+ {
+ }
+
+ ~WmaStream()
+ {
+ Dispose(false);
+ }
+
+ ///
+ /// Give the that describes the format of ouput data in each Read operation.
+ ///
+ public WaveFormat Format
+ {
+ get { return new WaveFormat(m_outputFormat.SampleRate, m_outputFormat.BitsPerSample, m_outputFormat.Channels); }
+ }
+
+ ///
+ /// IWMProfile of the input ASF file.
+ ///
+ public IWMProfile Profile
+ {
+ get { return (IWMProfile)m_reader; }
+ }
+
+ ///
+ /// IWMHeaderInfo related to the input ASF file.
+ ///
+ public IWMHeaderInfo HeaderInfo
+ {
+ get { return (IWMHeaderInfo)m_reader; }
+ }
+
+ ///
+ /// Recomended size of buffer in each operation
+ ///
+ public int SampleSize
+ {
+ get { return (int)m_sampleSize; }
+ }
+
+ ///
+ /// If Seek if allowed every seek operation must be to a value multiple of SeekAlign
+ ///
+ public long SeekAlign
+ {
+ get { return Math.Max(SampleTime2BytePosition(1), (long)m_outputFormat.BlockAlign); }
+ }
+
+ ///
+ /// Convert a time value in 100 nanosecond unit to a byte position
+ /// of byte array containing the PCM data.
+ ///
+ /// Sample time in 100 nanosecond units
+ /// Byte position
+ protected long SampleTime2BytePosition(ulong SampleTime)
+ {
+ ulong res = SampleTime * (ulong)m_outputFormat.AverageBytesPerSecond / 10000000;
+ //Align to sample position
+ res -= (res % (ulong)m_outputFormat.BlockAlign);
+ return (long)res;
+ }
+
+ ///
+ /// Returns the sample time in 100 nanosecond units corresponding to
+ /// byte position in a byte array of PCM data.
+ ///
+ /// Byte position
+ /// Sample time in 100 nanosecond units
+ protected ulong BytePosition2SampleTime(long pos)
+ {
+ //Align to sample position
+ pos -= (pos % (long)m_outputFormat.BlockAlign);
+ return (ulong)pos * 10000000 / (ulong)m_outputFormat.AverageBytesPerSecond;
+ }
+
+ ///
+ /// Index that give the string representation of the Metadata attribute whose
+ /// name is the string index. If the Metadata is not present returns null
.
+ /// This only return the file level Metadata info, to read stream level metadata use
+ ///
+ ///
+ ///
+ /// using (WmaStream str = new WmaStream("somefile.asf") )
+ /// {
+ /// string Title = str[WM.g_wszWMTitle];
+ /// if ( Title != null )
+ /// {
+ /// Console.WriteLine("Title: {0}", Title);
+ /// }
+ /// }
+ ///
+ ///
+ [System.Runtime.CompilerServices.IndexerName("Attributes")]
+ public string this[string AttrName]
+ {
+ get
+ {
+ WMHeaderInfo head = new WMHeaderInfo(HeaderInfo);
+ try
+ {
+ return head[AttrName].Value.ToString();
+ }
+ catch (COMException e)
+ {
+ if (e.ErrorCode == WM.ASF_E_NOTFOUND)
+ {
+ return null;
+ }
+ else
+ {
+ throw (e);
+ }
+ }
+ }
+ }
+
+ #region Private methods to interact with the WMF
+
+ private void Init(WaveFormat OutputFormat)
+ {
+ m_outputNumber = GetAudioOutputNumber(m_reader);
+ if (m_outputNumber == InvalidOuput)
+ {
+ throw new ArgumentException("An audio stream was not found");
+ }
+ int[] FormatIndexes = GetPCMOutputNumbers(m_reader, (uint)m_outputNumber);
+ if (FormatIndexes.Length == 0)
+ {
+ throw new ArgumentException("An audio stream was not found");
+ }
+ if (OutputFormat != null)
+ {
+ m_outputFormatNumber = -1;
+ for (int i = 0; i < FormatIndexes.Length; i++)
+ {
+ WaveFormat fmt = GetOutputFormat(m_reader, (uint)m_outputNumber, (uint)FormatIndexes[i]);
+ if (// (fmt.wFormatTag == OutputFormat.wFormatTag) &&
+ (fmt.AverageBytesPerSecond == OutputFormat.AverageBytesPerSecond) &&
+ (fmt.BlockAlign == OutputFormat.BlockAlign) &&
+ (fmt.Channels == OutputFormat.Channels) &&
+ (fmt.SampleRate == OutputFormat.SampleRate) &&
+ (fmt.BitsPerSample == OutputFormat.BitsPerSample))
+ {
+ m_outputFormatNumber = FormatIndexes[i];
+ m_outputFormat = fmt;
+ break;
+ }
+ }
+ if (m_outputFormatNumber < 0)
+ {
+ throw new ArgumentException("No PCM output found");
+ }
+ }
+ else
+ {
+ m_outputFormatNumber = FormatIndexes[0];
+ m_outputFormat = GetOutputFormat(m_reader, (uint)m_outputNumber, (uint)FormatIndexes[0]);
+ }
+ uint OutputCtns = 0;
+ m_reader.GetOutputCount(out OutputCtns);
+ ushort[] StreamNumbers = new ushort[OutputCtns];
+ WMT_STREAM_SELECTION[] StreamSelections = new WMT_STREAM_SELECTION[OutputCtns];
+ for (uint i = 0; i < OutputCtns; i++)
+ {
+ m_reader.GetStreamNumberForOutput(i, out StreamNumbers[i]);
+ if (i == m_outputNumber)
+ {
+ StreamSelections[i] = WMT_STREAM_SELECTION.WMT_ON;
+ m_outputStream = StreamNumbers[i];
+ m_reader.SetReadStreamSamples(m_outputStream, false);
+ }
+ else
+ {
+ StreamSelections[i] = WMT_STREAM_SELECTION.WMT_OFF;
+ }
+ }
+ m_reader.SetStreamsSelected((ushort)OutputCtns, StreamNumbers, StreamSelections);
+ IWMOutputMediaProps Props = null;
+ m_reader.GetOutputFormat((uint)m_outputNumber, (uint)m_outputFormatNumber, out Props);
+ m_reader.SetOutputProps((uint)m_outputNumber, Props);
+
+ int size = 0;
+ Props.GetMediaType(IntPtr.Zero, ref size);
+ IntPtr buffer = Marshal.AllocCoTaskMem(size);
+ try
+ {
+ WM_MEDIA_TYPE mt;
+ Props.GetMediaType(buffer, ref size);
+ mt = (WM_MEDIA_TYPE)Marshal.PtrToStructure(buffer, typeof(WM_MEDIA_TYPE));
+ m_sampleSize = mt.lSampleSize;
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(buffer);
+ Props = null;
+ }
+
+ m_seekable = false;
+ m_length = -1;
+ WMHeaderInfo head = new WMHeaderInfo(HeaderInfo);
+ try
+ {
+ m_seekable = (bool)head[WM.g_wszWMSeekable];
+ // Yuval Naveh
+ ulong nanoDuration = (ulong)head[WM.g_wszWMDuration];
+ m_duration = new TimeSpan((long)nanoDuration);
+ m_length = SampleTime2BytePosition(nanoDuration);
+ }
+ catch (COMException e)
+ {
+ if (e.ErrorCode != WM.ASF_E_NOTFOUND)
+ {
+ throw (e);
+ }
+ }
+
+ }
+
+ private static uint GetAudioOutputNumber(IWMSyncReader Reader)
+ {
+ uint res = InvalidOuput;
+ uint OutCounts = 0;
+ Reader.GetOutputCount(out OutCounts);
+ for (uint i = 0; i < OutCounts; i++)
+ {
+ IWMOutputMediaProps Props = null;
+ Reader.GetOutputProps(i, out Props);
+ Guid mt;
+ Props.GetType(out mt);
+ if (mt == MediaTypes.WMMEDIATYPE_Audio)
+ {
+ res = i;
+ break;
+ }
+ }
+ return res;
+ }
+
+ protected const uint WAVE_FORMAT_EX_SIZE = 18;
+
+ private static int[] GetPCMOutputNumbers(IWMSyncReader Reader, uint OutputNumber)
+ {
+ var result = new List();
+ uint FormatCount = 0;
+ Reader.GetOutputFormatCount(OutputNumber, out FormatCount);
+ int BufferSize = Marshal.SizeOf(typeof(WM_MEDIA_TYPE)) + Marshal.SizeOf(typeof(WaveFormat));
+ IntPtr buffer = Marshal.AllocCoTaskMem(BufferSize);
+ try
+ {
+ for (int i = 0; i < FormatCount; i++)
+ {
+ IWMOutputMediaProps Props = null;
+ int size = 0;
+ WM_MEDIA_TYPE mt;
+ Reader.GetOutputFormat(OutputNumber, (uint)i, out Props);
+ Props.GetMediaType(IntPtr.Zero, ref size);
+ if (size > BufferSize)
+ {
+ BufferSize = size;
+ Marshal.FreeCoTaskMem(buffer);
+ buffer = Marshal.AllocCoTaskMem(BufferSize);
+ }
+ Props.GetMediaType(buffer, ref size);
+ mt = (WM_MEDIA_TYPE)Marshal.PtrToStructure(buffer, typeof(WM_MEDIA_TYPE));
+ if ((mt.majortype == MediaTypes.WMMEDIATYPE_Audio) &&
+ (mt.subtype == MediaTypes.WMMEDIASUBTYPE_PCM) &&
+ (mt.formattype == MediaTypes.WMFORMAT_WaveFormatEx) &&
+ (mt.cbFormat >= WAVE_FORMAT_EX_SIZE))
+ {
+ result.Add(i);
+ }
+ }
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(buffer);
+ }
+ return result.ToArray();
+ }
+
+ private static WaveFormat GetOutputFormat(IWMSyncReader reader, uint outputNumber, uint formatNumber)
+ {
+ IWMOutputMediaProps Props = null;
+ int size = 0;
+ WaveFormat fmt = null;
+ reader.GetOutputFormat(outputNumber, formatNumber, out Props);
+ Props.GetMediaType(IntPtr.Zero, ref size);
+ IntPtr buffer = Marshal.AllocCoTaskMem(Math.Max(size, Marshal.SizeOf(typeof(WM_MEDIA_TYPE)) + Marshal.SizeOf(typeof(WaveFormat))));
+ try
+ {
+ Props.GetMediaType(buffer, ref size);
+ var mt = (WM_MEDIA_TYPE)Marshal.PtrToStructure(buffer, typeof(WM_MEDIA_TYPE));
+ if ((mt.majortype == MediaTypes.WMMEDIATYPE_Audio) &&
+ (mt.subtype == MediaTypes.WMMEDIASUBTYPE_PCM) &&
+ (mt.formattype == MediaTypes.WMFORMAT_WaveFormatEx) &&
+ (mt.cbFormat >= WAVE_FORMAT_EX_SIZE))
+ {
+ fmt = new WaveFormat(44100, 16, 2);
+ Marshal.PtrToStructure(mt.pbFormat, fmt);
+ }
+ else
+ {
+ throw new ArgumentException(string.Format("The format {0} of the output {1} is not a valid PCM format", formatNumber, outputNumber));
+ }
+ }
+ finally
+ {
+ Marshal.FreeCoTaskMem(buffer);
+ }
+ return fmt;
+ }
+ #endregion
+
+ #region Overrided Stream methods
+ public override void Close()
+ {
+ if (m_reader != null)
+ {
+ m_reader.Close();
+ m_reader = null;
+ }
+ base.Close();
+ }
+
+ private NSSBuffer m_BufferReader = null;
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (m_reader != null)
+ {
+ int read = 0;
+ if ((m_length > 0) && ((m_length - m_position) < count))
+ {
+ count = (int)(m_length - m_position);
+ }
+ if (m_BufferReader != null)
+ {
+ while ((m_BufferReader.Position < m_BufferReader.Length) && (read < count))
+ {
+ read += m_BufferReader.Read(buffer, offset, count);
+ }
+ }
+ while (read < count)
+ {
+ INSSBuffer sample = null;
+ ulong SampleTime = 0;
+ ulong Duration = 0;
+ uint Flags = 0;
+ try
+ {
+ m_reader.GetNextSample(m_outputStream, out sample, out SampleTime, out Duration, out Flags, out m_outputNumber, out m_outputStream);
+ }
+ catch (COMException e)
+ {
+ if (e.ErrorCode == WM.NS_E_NO_MORE_SAMPLES)
+ { //No more samples
+ if (m_outputFormat.BitsPerSample == 8)
+ {
+ while (read < count)
+ {
+ buffer[offset + read] = 0x80;
+ read++;
+ }
+ }
+ else
+ {
+ Array.Clear(buffer, offset + read, count - read);
+ read = count;
+ }
+ break;
+ }
+ else
+ {
+ throw (e);
+ }
+ }
+ m_BufferReader = new NSSBuffer(sample);
+ read += m_BufferReader.Read(buffer, offset + read, count - read);
+ }
+ if ((m_BufferReader != null) && (m_BufferReader.Position >= m_BufferReader.Length))
+ {
+ m_BufferReader = null;
+ }
+ m_position += read;
+ return read;
+ }
+ else
+ {
+ throw new ObjectDisposedException(this.ToString());
+ }
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ if (CanSeek)
+ {
+ switch (origin)
+ {
+ case SeekOrigin.Current:
+ offset += m_position;
+ break;
+ case SeekOrigin.End:
+ offset += m_length;
+ break;
+ }
+ if (offset == m_position)
+ {
+ return m_position; // :-)
+ }
+ if ((offset < 0) || (offset > m_length))
+ {
+ throw new ArgumentException("Offset out of range", "offset");
+ }
+ if ((offset % SeekAlign) > 0)
+ {
+ throw new ArgumentException(string.Format("Offset must be aligned by a value of SeekAlign ({0})", SeekAlign), "offset");
+ }
+ ulong SampleTime = BytePosition2SampleTime(offset);
+ m_reader.SetRange(SampleTime, 0);
+ m_position = offset;
+ m_BufferReader = null;
+ return offset;
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public override void Flush()
+ {
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException();
+ }
+
+ public override bool CanRead
+ {
+ get
+ {
+ if (m_reader != null)
+ {
+ return true;
+ }
+ else
+ {
+ throw new ObjectDisposedException(this.ToString());
+ }
+ }
+ }
+
+ public override bool CanSeek
+ {
+ get
+ {
+ if (m_reader != null)
+ {
+ return m_seekable && (m_length > 0);
+ }
+ else
+ {
+ throw new ObjectDisposedException(this.ToString());
+ }
+ }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public TimeSpan Duration
+ {
+ get
+ {
+ return m_duration;
+ }
+ }
+
+ public override long Length
+ {
+ get
+ {
+ if (m_reader != null)
+ {
+ if (CanSeek)
+ {
+ return m_length;
+ }
+ else
+ {
+ throw new NotSupportedException();
+ }
+ }
+ else
+ {
+ throw new ObjectDisposedException(this.ToString());
+ }
+ }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ if (m_reader != null)
+ {
+ return m_position;
+ }
+ else
+ {
+ throw new ObjectDisposedException(this.ToString());
+ }
+ }
+ set
+ {
+ Seek(value, SeekOrigin.Begin);
+ }
+ }
+ #endregion
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (m_reader != null)
+ {
+ m_reader.Close();
+ m_reader = null;
+ }
+ }
+ }
+
+ private IWMSyncReader m_reader = null;
+ private uint m_outputNumber;
+ private ushort m_outputStream;
+ private int m_outputFormatNumber;
+ private long m_position = 0;
+ private long m_length = -1;
+ private bool m_seekable = false;
+ private uint m_sampleSize = 0;
+ private WaveFormat m_outputFormat = null;
+ private const uint InvalidOuput = 0xFFFFFFFF;
+
+ private TimeSpan m_duration;
+ }
+
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WmaWriter.cs b/NAudio.WindowsMediaFormat/Interop/WmaWriter.cs
new file mode 100644
index 00000000..98d80ee5
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WmaWriter.cs
@@ -0,0 +1,253 @@
+// Widows Media Format Utils classes
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+// Modified for NAudio by Mark Heath
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// Audio Writer to write Windows Media Audio data to a stream.
+ ///
+ public class WmaWriter : IWMWriterSink, IDisposable
+ {
+ protected IWMWriter m_Writer;
+ private IWMProfile m_Profile;
+ private bool m_HeaderWrote = false;
+ private long m_HeaderPosition;
+ private uint m_HeaderLength;
+ private ulong m_MsAudioTime = 0;
+ private Stream m_outputStream;
+ private WaveFormat m_inputFormat;
+
+
+ public WmaWriter(Stream output, WaveFormat inputFormat, CodecFormat codecFormat)
+ : this(output,inputFormat,codecFormat.GetProfile())
+ {
+
+ }
+
+ ///
+ /// Create the writer indicating Metadata information
+ ///
+ /// Where resulting WMA string will be written
+ /// PCM format of input data received in method
+ /// IWMProfile that describe the resulting compressed stream
+ /// Array of structures describing the metadata information that will be in the result stream
+ public WmaWriter(Stream output, WaveFormat inputFormat, IWMProfile profile, WM_Attr[] MetadataAttributes)
+ {
+ this.m_outputStream = output;
+ this.m_inputFormat = inputFormat;
+ m_Writer = WM.CreateWriter();
+ IWMWriterAdvanced wa = (IWMWriterAdvanced)m_Writer;
+ wa.AddSink((IWMWriterSink)this);
+ m_Writer.SetProfile(profile);
+ uint inputs;
+ m_Writer.GetInputCount(out inputs);
+ if (inputs == 1)
+ {
+ IWMInputMediaProps InpProps;
+ Guid type;
+ m_Writer.GetInputProps(0, out InpProps);
+ InpProps.GetType(out type);
+ if (type == MediaTypes.WMMEDIATYPE_Audio)
+ {
+ WM_MEDIA_TYPE mt;
+ mt.majortype = MediaTypes.WMMEDIATYPE_Audio;
+ mt.subtype = MediaTypes.WMMEDIASUBTYPE_PCM;
+ mt.bFixedSizeSamples = true;
+ mt.bTemporalCompression = false;
+ mt.lSampleSize = (uint)inputFormat.BlockAlign;
+ mt.formattype = MediaTypes.WMFORMAT_WaveFormatEx;
+ mt.pUnk = IntPtr.Zero;
+ mt.cbFormat = (uint)Marshal.SizeOf(inputFormat);
+
+ GCHandle h = GCHandle.Alloc(inputFormat, GCHandleType.Pinned);
+ try
+ {
+ mt.pbFormat = h.AddrOfPinnedObject();
+ InpProps.SetMediaType(ref mt);
+ }
+ finally
+ {
+ h.Free();
+ }
+ m_Writer.SetInputProps(0, InpProps);
+ if (MetadataAttributes != null)
+ {
+ WMHeaderInfo info = new WMHeaderInfo((IWMHeaderInfo)m_Writer);
+ foreach (WM_Attr attr in MetadataAttributes)
+ {
+ info.SetAttribute(attr);
+ }
+ info = null;
+ }
+ m_Writer.BeginWriting();
+ m_Profile = profile;
+ }
+ else
+ {
+ throw new ArgumentException("Invalid profile", "profile");
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Invalid profile", "profile");
+ }
+ }
+ ///
+ /// Create the writer without metadata Information
+ ///
+ /// Where resulting WMA string will be written
+ /// PCM format of input data received in method
+ /// IWMProfile that describe the resulting compressed stream
+ public WmaWriter(Stream output, WaveFormat format, IWMProfile profile)
+ : this(output, format, profile, null)
+ {
+ }
+
+ #region IWMWriterSink implementation
+ public void OnHeader([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pHeader)
+ {
+ byte[] buffer;
+ uint Length;
+ if (pHeader is ManBuffer)
+ {
+ buffer = ((ManBuffer)pHeader).Buffer;
+ Length = ((ManBuffer)pHeader).UsedLength;
+ }
+ else
+ {
+ NSSBuffer b = new NSSBuffer(pHeader);
+ Length = b.Length;
+ buffer = new byte[Length];
+ b.Read(buffer, 0, (int)Length);
+ }
+ if (!m_HeaderWrote)
+ {
+ if (m_outputStream.CanSeek)
+ {
+ m_HeaderPosition = m_outputStream.Position;
+ m_HeaderLength = Length;
+ }
+ m_HeaderWrote = true;
+ m_outputStream.Write(buffer, 0, (int)Length);
+ }
+ else if (m_outputStream.CanSeek && (Length == m_HeaderLength))
+ {
+ long pos = m_outputStream.Position;
+ m_outputStream.Position = m_HeaderPosition;
+ m_outputStream.Write(buffer, 0, (int)Length);
+ m_outputStream.Position = pos;
+ }
+ }
+
+ public void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime)
+ {
+ pfRealTime = false;
+ }
+
+ public void AllocateDataUnit([In] uint cbDataUnit,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppDataUnit)
+ {
+ ppDataUnit = (INSSBuffer)(new ManBuffer(cbDataUnit));
+ }
+
+ public void OnDataUnit([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pDataUnit)
+ {
+ byte[] buffer;
+ int Length;
+ if (pDataUnit is ManBuffer)
+ {
+ buffer = ((ManBuffer)pDataUnit).Buffer;
+ Length = (int)((ManBuffer)pDataUnit).UsedLength;
+ }
+ else
+ {
+ NSSBuffer b = new NSSBuffer(pDataUnit);
+ Length = (int)b.Length;
+ buffer = new byte[Length];
+ b.Read(buffer, 0, Length);
+ }
+ m_outputStream.Write(buffer, 0, Length);
+ }
+
+ public void OnEndWriting()
+ {
+ }
+
+ #endregion
+
+ public void Dispose()
+ {
+ try
+ {
+ if (m_Writer != null)
+ {
+ m_Writer.EndWriting();
+ IWMWriterAdvanced wa = (IWMWriterAdvanced)m_Writer;
+ wa.RemoveSink((IWMWriterSink)this);
+ m_Writer = null;
+ m_Profile = null;
+ }
+ }
+ finally
+ {
+ m_outputStream.Close();
+ }
+ }
+
+
+ ///
+ /// Return the optimal size of buffer in each write operations. Other value could be
+ /// more optimal. The only requirement for buffer size is that it must be multiple
+ /// of PCM sample size
+ ///
+ /// Size equivalent to 100 milliseconds.
+ public int GetOptimalBufferSize()
+ {
+ int res = m_inputFormat.AverageBytesPerSecond / 10;
+ res += (res % m_inputFormat.BlockAlign);
+ return res;
+ }
+
+ ///
+ /// Write a buffer of uncompressed PCM data to the buffer.
+ ///
+ /// Byte array defining the buffer to write.
+ /// Index of first value to write
+ /// NUmber of byte to write. Must be multiple of PCM sample size
+ public void Write(byte[] buffer, int index, int count)
+ {
+ if ((count % m_inputFormat.BlockAlign) == 0)
+ {
+ INSSBuffer IBuff;
+ NSSBuffer NssBuff;
+ m_Writer.AllocateSample((uint)count, out IBuff);
+ NssBuff = new NSSBuffer(IBuff);
+ NssBuff.Write(buffer, index, count);
+ NssBuff.Length = (uint)count;
+ m_Writer.WriteSample(0, m_MsAudioTime * 10000, 0, IBuff);
+ m_MsAudioTime += ((ulong)count * 1000) / (ulong)m_inputFormat.AverageBytesPerSecond;
+ }
+ else
+ {
+ throw new ArgumentException(string.Format("Invalid buffer size. Buffer size must be aligned to {0} bytes.", m_inputFormat.BlockAlign), "count");
+ }
+ }
+ }
+}
+
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriter.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriter.cs
new file mode 100644
index 00000000..715714d0
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriter.cs
@@ -0,0 +1,58 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BD4-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriter
+ {
+ void SetProfileByID([In] ref Guid guidProfile);
+ void SetProfile([In, MarshalAs(UnmanagedType.Interface)] IWMProfile pProfile);
+ void SetOutputFilename([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ void GetInputCount([Out] out uint pcInputs);
+ void GetInputProps([In] uint dwInputNum,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMInputMediaProps ppInput);
+ void SetInputProps([In] uint dwInputNum,
+ [In, MarshalAs(UnmanagedType.Interface)] IWMInputMediaProps pInput);
+ void GetInputFormatCount([In] uint dwInputNumber, [Out] out uint pcFormats);
+ void GetInputFormat([In] uint dwInputNumber,
+ [In] uint dwFormatNumber,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMInputMediaProps pProps);
+ void BeginWriting();
+ void EndWriting();
+ void AllocateSample([In] uint dwSampleSize,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppSample);
+ void WriteSample([In] uint dwInputNum,
+ [In] ulong cnsSampleTime,
+ [In] uint dwFlags,
+ [In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pSample);
+ void Flush();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced.cs
new file mode 100644
index 00000000..2ff78281
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced.cs
@@ -0,0 +1,56 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BE3-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterAdvanced
+ {
+ void GetSinkCount([Out] out uint pcSinks);
+
+ void GetSink([In] uint dwSinkNum,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMWriterSink ppSink);
+ void AddSink([In, MarshalAs(UnmanagedType.Interface)] IWMWriterSink pSink);
+ void RemoveSink([In, MarshalAs(UnmanagedType.Interface)] IWMWriterSink pSink);
+ void WriteStreamSample([In] ushort wStreamNum,
+ [In] ulong cnsSampleTime,
+ [In] uint msSampleSendTime,
+ [In] ulong cnsSampleDuration,
+ [In] uint dwFlags,
+ [In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pSample);
+ void SetLiveSource([MarshalAs(UnmanagedType.Bool)]bool fIsLiveSource);
+ void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ void GetWriterTime([Out] out ulong pcnsCurrentTime);
+ void GetStatistics([In] ushort wStreamNum,
+ [Out] out WM_WRITER_STATISTICS pStats);
+ void SetSyncTolerance([In] uint msWindow);
+ void GetSyncTolerance([Out] out uint pmsWindow);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced2.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced2.cs
new file mode 100644
index 00000000..9dff88d2
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced2.cs
@@ -0,0 +1,67 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("962dc1ec-c046-4db8-9cc7-26ceae500817")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterAdvanced2 : IWMWriterAdvanced
+ {
+ //IWMWriterAdvanced
+ new void GetSinkCount([Out] out uint pcSinks);
+ new void GetSink([In] uint dwSinkNum,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMWriterSink ppSink);
+ new void AddSink([In, MarshalAs(UnmanagedType.Interface)] IWMWriterSink pSink);
+ new void RemoveSink([In, MarshalAs(UnmanagedType.Interface)] IWMWriterSink pSink);
+ new void WriteStreamSample([In] ushort wStreamNum,
+ [In] ulong cnsSampleTime,
+ [In] uint msSampleSendTime,
+ [In] ulong cnsSampleDuration,
+ [In] uint dwFlags,
+ [In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pSample);
+ new void SetLiveSource([MarshalAs(UnmanagedType.Bool)]bool fIsLiveSource);
+ new void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ new void GetWriterTime([Out] out ulong pcnsCurrentTime);
+ new void GetStatistics([In] ushort wStreamNum,
+ [Out] out WM_WRITER_STATISTICS pStats);
+ new void SetSyncTolerance([In] uint msWindow);
+ new void GetSyncTolerance([Out] out uint pmsWindow);
+ //IWMWriterAdvanced2
+ void GetInputSetting([In] uint dwInputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ [In, Out] ref ushort pcbLength);
+ void SetInputSetting([In] uint dwInputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] byte[] pValue,
+ [In] ushort cbLength);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced3.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced3.cs
new file mode 100644
index 00000000..f18e2a5f
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterAdvanced3.cs
@@ -0,0 +1,71 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("2cd6492d-7c37-4e76-9d3b-59261183a22e")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterAdvanced3 : IWMWriterAdvanced2
+ {
+ //IWMWriterAdvanced
+ new void GetSinkCount([Out] out uint pcSinks);
+ new void GetSink([In] uint dwSinkNum,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IWMWriterSink ppSink);
+ new void AddSink([In, MarshalAs(UnmanagedType.Interface)] IWMWriterSink pSink);
+ new void RemoveSink([In, MarshalAs(UnmanagedType.Interface)] IWMWriterSink pSink);
+ new void WriteStreamSample([In] ushort wStreamNum,
+ [In] ulong cnsSampleTime,
+ [In] uint msSampleSendTime,
+ [In] ulong cnsSampleDuration,
+ [In] uint dwFlags,
+ [In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pSample);
+ new void SetLiveSource([MarshalAs(UnmanagedType.Bool)]bool fIsLiveSource);
+ new void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ new void GetWriterTime([Out] out ulong pcnsCurrentTime);
+ new void GetStatistics([In] ushort wStreamNum,
+ [Out] out WM_WRITER_STATISTICS pStats);
+ new void SetSyncTolerance([In] uint msWindow);
+ new void GetSyncTolerance([Out] out uint pmsWindow);
+ //IWMWriterAdvanced2
+ new void GetInputSetting([In] uint dwInputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [Out] out WMT_ATTR_DATATYPE pType,
+ [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pValue,
+ [In, Out] ref ushort pcbLength);
+ new void SetInputSetting([In] uint dwInputNum,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pszName,
+ [In] WMT_ATTR_DATATYPE Type,
+ [In, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)] byte[] pValue,
+ [In] ushort cbLength);
+ //IWMWriterAdvanced3
+ void GetStatisticsEx([In] ushort wStreamNum,
+ [Out] out WM_WRITER_STATISTICS_EX pStats);
+ void SetNonBlocking();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink.cs
new file mode 100644
index 00000000..bd6d6245
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink.cs
@@ -0,0 +1,46 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BE5-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterFileSink : IWMWriterSink
+ {
+ //IWMWriterSink
+ new void OnHeader([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pHeader);
+ new void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ new void AllocateDataUnit([In] uint cbDataUnit,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppDataUnit);
+ new void OnDataUnit([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pDataUnit);
+ new void OnEndWriting();
+ //IWMWriterFileSink
+ void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink2.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink2.cs
new file mode 100644
index 00000000..f3ef3349
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink2.cs
@@ -0,0 +1,54 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("14282BA7-4AEF-4205-8CE5-C229035A05BC")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterFileSink2 : IWMWriterFileSink
+ {
+ //IWMWriterSink
+ new void OnHeader([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pHeader);
+ new void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ new void AllocateDataUnit([In] uint cbDataUnit,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppDataUnit);
+ new void OnDataUnit([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pDataUnit);
+ new void OnEndWriting();
+ //IWMWriterFileSink
+ new void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ //IWMWriterFileSink2
+ void Start([In] ulong cnsStartTime);
+ void Stop([In] ulong cnsStopTime);
+ void IsStopped([Out, MarshalAs(UnmanagedType.Bool)] out bool pfStopped);
+ void GetFileDuration([Out] out ulong pcnsDuration);
+ void GetFileSize([Out] out ulong pcbFile);
+ void Close();
+ void IsClosed([Out, MarshalAs(UnmanagedType.Bool)] out bool pfClosed);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink3.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink3.cs
new file mode 100644
index 00000000..7c463761
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterFileSink3.cs
@@ -0,0 +1,65 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("3FEA4FEB-2945-47A7-A1DD-C53A8FC4C45C")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterFileSink3 : IWMWriterFileSink2
+ {
+ //IWMWriterSink
+ new void OnHeader([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pHeader);
+ new void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ new void AllocateDataUnit([In] uint cbDataUnit,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppDataUnit);
+ new void OnDataUnit([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pDataUnit);
+ new void OnEndWriting();
+ //IWMWriterFileSink
+ new void Open([In, MarshalAs(UnmanagedType.LPWStr)] string pwszFilename);
+ //IWMWriterFileSink2
+ new void Start([In] ulong cnsStartTime);
+ new void Stop([In] ulong cnsStopTime);
+ new void IsStopped([Out, MarshalAs(UnmanagedType.Bool)] out bool pfStopped);
+ new void GetFileDuration([Out] out ulong pcnsDuration);
+ new void GetFileSize([Out] out ulong pcbFile);
+ new void Close();
+ new void IsClosed([Out, MarshalAs(UnmanagedType.Bool)] out bool pfClosed);
+ //IWMWriterFileSink3
+ void SetAutoIndexing([In, MarshalAs(UnmanagedType.Bool)] bool fDoAutoIndexing);
+ void GetAutoIndexing([Out, MarshalAs(UnmanagedType.Bool)] out bool pfAutoIndexing);
+ void SetControlStream([In] ushort wStreamNumber,
+ [In, MarshalAs(UnmanagedType.Bool)] bool fShouldControlStartAndStop);
+ void GetMode([Out] out uint pdwFileSinkMode);
+ void OnDataUnitEx([In] ref WMT_FILESINK_DATA_UNIT pFileSinkDataUnit);
+ void SetUnbufferedIO([In, MarshalAs(UnmanagedType.Bool)] bool fUnbufferedIO,
+ [In, MarshalAs(UnmanagedType.Bool)] bool fRestrictMemUsage);
+ void GetUnbufferedIO([Out, MarshalAs(UnmanagedType.Bool)] out bool pfUnbufferedIO);
+ void CompleteOperations();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterNetworkSink.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterNetworkSink.cs
new file mode 100644
index 00000000..7e6ddfdd
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterNetworkSink.cs
@@ -0,0 +1,54 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BE7-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterNetworkSink : IWMWriterSink
+ {
+ //IWMWriterSink
+ new void OnHeader([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pHeader);
+ new void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ new void AllocateDataUnit([In] uint cbDataUnit,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppDataUnit);
+ new void OnDataUnit([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pDataUnit);
+ new void OnEndWriting();
+ //IWMWriterNetworkSink
+ void SetMaximumClients([In] uint dwMaxClients);
+ void GetMaximumClients([Out] out uint pdwMaxClients);
+ void SetNetworkProtocol([In] WMT_NET_PROTOCOL protocol);
+ void GetNetworkProtocol([Out] out WMT_NET_PROTOCOL pProtocol);
+ void GetHostURL([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszURL,
+ [In, Out] ref uint pcchURL);
+ void Open([In, Out] ref uint pdwPortNum);
+ void Disconnect();
+ void Close();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterPreprocess.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterPreprocess.cs
new file mode 100644
index 00000000..37016c5f
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterPreprocess.cs
@@ -0,0 +1,49 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("fc54a285-38c4-45b5-aa23-85b9f7cb424b")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterPreprocess
+ {
+ void GetMaxPreprocessingPasses([In] uint dwInputNum,
+ [In] uint dwFlags,
+ [Out] out uint pdwMaxNumPasses);
+ void SetNumPreprocessingPasses([In] uint dwInputNum,
+ [In] uint dwFlags,
+ [In] uint dwNumPasses);
+ void BeginPreprocessingPass([In] uint dwInputNum, [In] uint dwFlags);
+ void PreprocessSample([In] uint dwInputNum,
+ [In] ulong cnsSampleTime,
+ [In] uint dwFlags,
+ [In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pSample);
+ void EndPreprocessingPass([In] uint dwInputNum, [In] uint dwFlags);
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterPushSink.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterPushSink.cs
new file mode 100644
index 00000000..2e603b9b
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterPushSink.cs
@@ -0,0 +1,50 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("dc10e6a5-072c-467d-bf57-6330a9dde12a")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterPushSink : IWMWriterSink
+ {
+ //IWMWriterSink
+ new void OnHeader([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pHeader);
+ new void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ new void AllocateDataUnit([In] uint cbDataUnit,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppDataUnit);
+ new void OnDataUnit([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pDataUnit);
+ new void OnEndWriting();
+ //IWMWriterPushSink
+ void Connect([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL,
+ [In, MarshalAs(UnmanagedType.LPWStr)] string pwszTemplateURL,
+ [In, MarshalAs(UnmanagedType.Bool)] bool fAutoDestroy);
+ void Disconnect();
+ void EndSession();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterSink.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterSink.cs
new file mode 100644
index 00000000..724a4828
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/IWMWriterSink.cs
@@ -0,0 +1,43 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [ComImport]
+ [Guid("96406BE4-2B2B-11d3-B36B-00C04F6108FF")]
+ [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IWMWriterSink
+ {
+ void OnHeader([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pHeader);
+ void IsRealTime([Out, MarshalAs(UnmanagedType.Bool)] out bool pfRealTime);
+ void AllocateDataUnit([In] uint cbDataUnit,
+ [Out, MarshalAs(UnmanagedType.Interface)] out INSSBuffer ppDataUnit);
+ void OnDataUnit([In, MarshalAs(UnmanagedType.Interface)] INSSBuffer pDataUnit);
+ void OnEndWriting();
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_BUFFER_SEGMENT.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_BUFFER_SEGMENT.cs
new file mode 100644
index 00000000..aaaefe4d
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_BUFFER_SEGMENT.cs
@@ -0,0 +1,38 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WMT_BUFFER_SEGMENT
+ {
+ public INSSBuffer pBuffer;
+ public uint cbOffset;
+ public uint cbLength;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_FILESINK_DATA_UNIT.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_FILESINK_DATA_UNIT.cs
new file mode 100644
index 00000000..e04358c3
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_FILESINK_DATA_UNIT.cs
@@ -0,0 +1,42 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WMT_FILESINK_DATA_UNIT
+ {
+ public WMT_BUFFER_SEGMENT packetHeaderBuffer;
+ public uint cPayloads;
+ /*WMT_BUFFER_SEGMENT* */
+ public IntPtr pPayloadHeaderBuffers;
+ public uint cPayloadDataFragments;
+ /*WMT_PAYLOAD_FRAGMENT* */
+ public IntPtr pPayloadDataFragments;
+ };
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_PAYLOAD_FRAGMENT.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_PAYLOAD_FRAGMENT.cs
new file mode 100644
index 00000000..9b6227c2
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WMT_PAYLOAD_FRAGMENT.cs
@@ -0,0 +1,37 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WMT_PAYLOAD_FRAGMENT
+ {
+ public uint dwPayloadIndex;
+ public WMT_BUFFER_SEGMENT segmentData;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/WM_WRITER_STATISTICS.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WM_WRITER_STATISTICS.cs
new file mode 100644
index 00000000..779a36bf
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WM_WRITER_STATISTICS.cs
@@ -0,0 +1,46 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WM_WRITER_STATISTICS
+ {
+ public ulong qwSampleCount;
+ public ulong qwByteCount;
+ public ulong qwDroppedSampleCount;
+ public ulong qwDroppedByteCount;
+ public uint dwCurrentBitrate;
+ public uint dwAverageBitrate;
+ public uint dwExpectedBitrate;
+ public uint dwCurrentSampleRate;
+ public uint dwAverageSampleRate;
+ public uint dwExpectedSampleRate;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/Interop/WriterTypes/WM_WRITER_STATISTICS_EX.cs b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WM_WRITER_STATISTICS_EX.cs
new file mode 100644
index 00000000..fa1a40b7
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Interop/WriterTypes/WM_WRITER_STATISTICS_EX.cs
@@ -0,0 +1,42 @@
+#region Original License
+//Widows Media Format Interfaces
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
+// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
+// PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
+// REMAINS UNCHANGED.
+//
+// Email: yetiicb@hotmail.com
+//
+// Copyright (C) 2002-2004 Idael Cardoso.
+//
+#endregion
+
+#region Code Modifications Note
+// Yuval Naveh, 2010
+// Note - The code below has been changed and fixed from its original form.
+// Changes include - Formatting, Layout, Coding standards and removal of compilation warnings
+
+// Mark Heath, 2010 - modified for inclusion in NAudio
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.WindowsMediaFormat
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WM_WRITER_STATISTICS_EX
+ {
+ public uint dwBitratePlusOverhead;
+ public uint dwCurrentSampleDropRateInQueue;
+ public uint dwCurrentSampleDropRateInCodec;
+ public uint dwCurrentSampleDropRateInMultiplexer;
+ public uint dwTotalSampleDropsInQueue;
+ public uint dwTotalSampleDropsInCodec;
+ public uint dwTotalSampleDropsInMultiplexer;
+ }
+}
diff --git a/NAudio.WindowsMediaFormat/NAudio.WindowsMediaFormat.csproj b/NAudio.WindowsMediaFormat/NAudio.WindowsMediaFormat.csproj
new file mode 100644
index 00000000..34ee632a
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/NAudio.WindowsMediaFormat.csproj
@@ -0,0 +1,187 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}
+ Library
+ Properties
+ NAudio.WindowsMediaFormat
+ NAudio.WindowsMediaFormat
+ v3.5
+ 512
+
+
+ 3.5
+
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+ Client
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AllRules.ruleset
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AllRules.ruleset
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}
+ NAudio
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudio.WindowsMediaFormat/Properties/AssemblyInfo.cs b/NAudio.WindowsMediaFormat/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..29694838
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NAudio.WindowsMediaFormat")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NAudio.WindowsMediaFormat")]
+[assembly: AssemblyCopyright("Copyright © 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("48e45559-0547-498d-9b97-66e08b26b3fa")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.1.0")]
+[assembly: AssemblyFileVersion("1.0.1.0")]
diff --git a/NAudio.WindowsMediaFormat/WmaFileReader.cs b/NAudio.WindowsMediaFormat/WmaFileReader.cs
new file mode 100644
index 00000000..3f5f0ce2
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/WmaFileReader.cs
@@ -0,0 +1,138 @@
+#region © Copyright 2010 Yuval Naveh. MIT.
+/* Copyright (c) 2010, Yuval Naveh
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudio.WindowsMediaFormat
+{
+ ///
+ /// NAudio reader for WMA Vorbis files
+ ///
+ ///
+ /// Written By Yuval Naveh
+ ///
+ public class WMAFileReader : WaveStream
+ {
+ #region Constructors
+
+ /// Constructor - Supports opening a WMA file
+ public WMAFileReader(string wmaFileName)
+ {
+ m_wmaStream = new WmaStream(wmaFileName);
+ m_waveFormat = m_wmaStream.Format;
+ }
+
+ #endregion
+
+ #region WaveStream Overrides - Implement logic which is specific to WMA
+
+ ///
+ /// This is the length in bytes of data available to be read out from the Read method
+ /// (i.e. the decompressed WMA length)
+ /// n.b. this may return 0 for files whose length is unknown
+ ///
+ public override long Length
+ {
+ get
+ {
+ return m_wmaStream.Length;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return m_waveFormat; }
+ }
+
+ ///
+ ///
+ ///
+ public override long Position
+ {
+ get
+ {
+ return m_wmaStream.Position;
+ }
+ set
+ {
+ lock (m_repositionLock)
+ {
+ m_wmaStream.Position = value;
+ }
+ }
+ }
+
+ ///
+ /// Reads decompressed PCM data from our WMA file.
+ ///
+ public override int Read(byte[] sampleBuffer, int offset, int numBytes)
+ {
+ int bytesRead = 0;
+ lock (m_repositionLock)
+ {
+ // Read PCM bytes from the WMA File into the sample buffer
+ bytesRead = m_wmaStream.Read(sampleBuffer, offset, numBytes);
+ }
+
+ return bytesRead;
+ }
+
+ #endregion
+
+ #region Dispose
+
+ ///
+ /// Disposes this WaveStream
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (m_wmaStream != null)
+ {
+ m_wmaStream.Close();
+ m_wmaStream.Dispose();
+ m_wmaStream = null;
+ }
+ }
+ base.Dispose(disposing);
+ }
+
+ #endregion
+
+ #region Private Members
+
+ private WaveFormat m_waveFormat;
+ private object m_repositionLock = new object();
+ private WmaStream m_wmaStream;
+
+ #endregion
+ }
+
+}
diff --git a/NAudio.WindowsMediaFormat/app.config b/NAudio.WindowsMediaFormat/app.config
new file mode 100644
index 00000000..a89b9c67
--- /dev/null
+++ b/NAudio.WindowsMediaFormat/app.config
@@ -0,0 +1,3 @@
+
+
+
diff --git a/NAudio.Wma.nuspec b/NAudio.Wma.nuspec
new file mode 100644
index 00000000..57c45390
--- /dev/null
+++ b/NAudio.Wma.nuspec
@@ -0,0 +1,23 @@
+
+
+
+ NAudio.Wma
+ 1.0.1
+ Mark Heath, Yuval Naveh
+ Mark Heath
+ http://naudio.codeplex.com/license
+ http://naudio.codeplex.com/
+ false
+ WMA Extension for NAudio
+
+ C# .NET audio sound WMA
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudio.nuspec b/NAudio.nuspec
new file mode 100644
index 00000000..d4b94144
--- /dev/null
+++ b/NAudio.nuspec
@@ -0,0 +1,23 @@
+
+
+
+ NAudio
+ 1.7.2
+ Mark Heath
+ Mark Heath
+ http://naudio.codeplex.com/license
+ http://naudio.codeplex.com/
+ false
+ NAudio, an audio library for .NET
+
+ C# .NET audio sound
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudio.proj b/NAudio.proj
new file mode 100644
index 00000000..df124fe0
--- /dev/null
+++ b/NAudio.proj
@@ -0,0 +1,90 @@
+
+
+ .\.build
+
+
+
+
+
+ packages\NUnit.2.5.10.11092\tools
+ BuildArtefacts\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudio.sln b/NAudio.sln
new file mode 100644
index 00000000..32258e9c
--- /dev/null
+++ b/NAudio.sln
@@ -0,0 +1,169 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudio", "NAudio\NAudio.csproj", "{DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MidiFileConverter", "MidiFileConverter\MidiFileConverter.csproj", "{5B5897BD-A423-4AF8-8A59-1C1372ED77DB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MixDiff", "MixDiff\MixDiff.csproj", "{1293DD10-378A-4370-AEE2-AA1E9E87039B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudioTests", "NAudioTests\NAudioTests.csproj", "{5080281A-F9A1-403F-85C7-0DFF6839B07B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudioDemo", "NAudioDemo\NAudioDemo.csproj", "{C37A547B-F31E-45FB-870A-CFA704D06152}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AudioFileInspector", "AudioFileInspector\AudioFileInspector.csproj", "{D29C1659-635C-497B-847E-FE9A5A69ED03}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudio.WindowsMediaFormat", "NAudio.WindowsMediaFormat\NAudio.WindowsMediaFormat.csproj", "{1868FC77-FD6F-4881-9CF5-DE7451806BFA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudioWpfDemo", "NAudioWpfDemo\NAudioWpfDemo.csproj", "{A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F91DCE20-90A9-49A8-8773-CACECE651F37}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudio.Win8", "NAudio.Win8\NAudio.Win8.csproj", "{90543F38-E793-40C3-972D-3271EBF1DEF4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NAudioWin8Demo", "NAudioWin8Demo\NAudioWin8Demo.csproj", "{03A0E22E-5B00-4B87-9CDF-20CC121DCF03}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".build", ".build", "{FA54AC1B-F7DB-41A3-9292-C4CEB5B0BA2F}"
+ ProjectSection(SolutionItems) = preProject
+ Build.proj = Build.proj
+ .build\MSBuild.Community.Tasks.dll = .build\MSBuild.Community.Tasks.dll
+ .build\MSBuild.Community.Tasks.targets = .build\MSBuild.Community.Tasks.targets
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|ARM = Debug|ARM
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|ARM = Release|ARM
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Release|ARM.ActiveCfg = Release|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Release|x64.ActiveCfg = Release|Any CPU
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}.Release|x86.ActiveCfg = Release|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Release|ARM.ActiveCfg = Release|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Release|x64.ActiveCfg = Release|Any CPU
+ {5B5897BD-A423-4AF8-8A59-1C1372ED77DB}.Release|x86.ActiveCfg = Release|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Release|ARM.ActiveCfg = Release|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Release|x64.ActiveCfg = Release|Any CPU
+ {1293DD10-378A-4370-AEE2-AA1E9E87039B}.Release|x86.ActiveCfg = Release|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Release|ARM.ActiveCfg = Release|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Release|x64.ActiveCfg = Release|Any CPU
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}.Release|x86.ActiveCfg = Release|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Release|ARM.ActiveCfg = Release|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Release|x64.ActiveCfg = Release|Any CPU
+ {C37A547B-F31E-45FB-870A-CFA704D06152}.Release|x86.ActiveCfg = Release|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Release|ARM.ActiveCfg = Release|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Release|x64.ActiveCfg = Release|Any CPU
+ {D29C1659-635C-497B-847E-FE9A5A69ED03}.Release|x86.ActiveCfg = Release|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Release|ARM.ActiveCfg = Release|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Release|x64.ActiveCfg = Release|Any CPU
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}.Release|x86.ActiveCfg = Release|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Release|ARM.ActiveCfg = Release|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Release|x64.ActiveCfg = Release|Any CPU
+ {A7B74F85-D353-4ED4-A321-E6E4AD4D7D32}.Release|x86.ActiveCfg = Release|Any CPU
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|ARM.ActiveCfg = Debug|ARM
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|ARM.Build.0 = Debug|ARM
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|x64.ActiveCfg = Debug|x64
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|x64.Build.0 = Debug|x64
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|x86.ActiveCfg = Debug|x86
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Debug|x86.Build.0 = Debug|x86
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|ARM.ActiveCfg = Release|ARM
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|ARM.Build.0 = Release|ARM
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|x64.ActiveCfg = Release|x64
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|x64.Build.0 = Release|x64
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|x86.ActiveCfg = Release|x86
+ {90543F38-E793-40C3-972D-3271EBF1DEF4}.Release|x86.Build.0 = Release|x86
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|ARM.ActiveCfg = Debug|ARM
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|ARM.Build.0 = Debug|ARM
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|ARM.Deploy.0 = Debug|ARM
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|x64.ActiveCfg = Debug|x64
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|x64.Build.0 = Debug|x64
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|x64.Deploy.0 = Debug|x64
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|x86.ActiveCfg = Debug|x86
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|x86.Build.0 = Debug|x86
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Debug|x86.Deploy.0 = Debug|x86
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|ARM.ActiveCfg = Release|ARM
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|ARM.Build.0 = Release|ARM
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|ARM.Deploy.0 = Release|ARM
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|x64.ActiveCfg = Release|x64
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|x64.Build.0 = Release|x64
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|x64.Deploy.0 = Release|x64
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|x86.ActiveCfg = Release|x86
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|x86.Build.0 = Release|x86
+ {03A0E22E-5B00-4B87-9CDF-20CC121DCF03}.Release|x86.Deploy.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/NAudio.sln.DotSettings b/NAudio.sln.DotSettings
new file mode 100644
index 00000000..e3bb2d5c
--- /dev/null
+++ b/NAudio.sln.DotSettings
@@ -0,0 +1,6 @@
+
+ System
+ System.Linq
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
\ No newline at end of file
diff --git a/NAudio/Changes.xml b/NAudio/Changes.xml
new file mode 100644
index 00000000..ee7956e9
--- /dev/null
+++ b/NAudio/Changes.xml
@@ -0,0 +1,975 @@
+
+
+
+ 0.0.0.1
+ 9 Dec 2002
+ Initial version, basic mixer interop
+
+
+ 0.0.0.2
+ 10 Dec 2002
+ Added MIDI interop, MMException, more mixer classes
+
+
+ 0.0.0.3
+ 12 Dec 2002
+ Improvements to Mixer interop & classes
+
+
+ 0.0.0.4
+ 13 Dec 2002
+ Added basic WaveOut interop & classes
+
+
+ 0.0.0.5
+ 14 Dec 2002
+ Mixer bugfixes for mixer app
+
+
+ 0.0.0.6
+ 15 Dec 2002
+ More mixer bugfixes and design improvements
+
+
+ 0.0.0.7
+ 19 Dec 2002
+ Mixer interop bug fixes
+
+
+ 0.0.0.8
+ 20 Dec 2002
+ Wave functions improved
+
+
+ 0.0.0.9
+ 22 Dec 2002
+ Got wave playing working better
+
+
+ 0.0.1.10
+ 23 Dec 2002
+ Added pause and stop for WaveOut
+
+
+ 0.0.1.11
+ 24 Dec 2002
+ Improved class design trying to fix WaveOut bug (waveout callback was being GCed)
+
+
+ 0.0.1.12
+ 27 Dec 2002
+ Generic WaveStream class and WaveFileReader
+
+
+ 0.0.1.13
+ 5 Feb 2003
+ Improvements to WaveStream class
+ SoundFont library merged
+
+
+ 0.0.1.14
+ 27 Oct 2003
+ Converted to Visual Studio .NET
+
+
+ 0.0.1.15
+ 5 Dec 2003
+ Merged JavaLayer
+
+
+ 0.2.20.0
+ 25 Feb 2005
+ Merged newer SoundFontLib, MidiLib, Ogg, Acm
+
+
+ 0.2.21.0
+ 25 Feb 2005
+ Skip backwards and forwards in wav file
+ WavPlayer trackBar indicates progress
+ Allows trackBar repositioning
+
+
+ 0.2.22.0
+ 28 Feb 2005
+ WavePlayer show current time in hh:mm:ss
+ Can start playing from any point in the file
+
+
+ 0.2.23.0
+ 1 Mar 2005
+ More ACM stream conversion interop
+
+
+ 0.2.24.0
+ 2 Mar 2005
+ More ACM interop improvements
+ WaveFormatConversionStream class
+ WaveStream no longer inherits from Stream
+ AcmStream class
+ waveOutDevCaps interop
+ Improvements to WaveFileWriter
+
+
+ 0.2.25.0
+ 3 Mar 2005
+ AcmStream and AcmStreamHeader bug fixes
+ Improvements to WaveFileReader and WaveFileWriter
+ PCM to PCM offline conversion working
+
+
+ 0.2.26.0
+ 3 Mar 2005
+ Very basic ability to play converted streams in realtime
+
+
+ 0.2.27.0
+ 4 Mar 2005
+ Initial version of Renaissance GSM conversion stream
+
+
+ 0.2.28.0
+ 7 Mar 2005
+ Fix to WaveFileWriter
+ More disposable pattern
+ WaveFileConversionStream can convert files offline
+ WaveStreams can now recommend a read buffer size based on latency
+
+
+ 0.2.29.0
+ 8 Mar 2005
+ Offline Renaissance GSM stream conversion working
+ WaveOut takes a latency now
+ MmException improvement
+
+
+ 0.3.30.0
+ 8 Mar 2005
+ Greatly improved the ability to calculate appropriate buffer sizes
+ Realtime GSM decoding is now working
+
+
+ 0.3.31.0
+ 14 Mar 2005
+ Some changes recommended by FxCop
+
+
+ 0.3.32.0
+ 15 Mar 2005
+ More changes recommended by FxCop
+
+
+ 0.3.33.0
+ 15 Mar 2005
+ namespace changed to NAudio
+
+
+ 0.3.34.0
+ 23 Mar 2005
+ XML documentation, FxCop fixes, Namespace improvements
+ WaveFormat constructor from a BinaryReader
+
+
+ 0.3.35.0
+ 23 Mar 2005
+ WaveChannel and WaveMixerStream
+
+
+ 0.3.36.0
+ 24 Mar 2005
+ More namespace improvements
+ More XML documentation
+
+
+ 0.3.37.0
+ 6 May 2005
+ More XML documentation
+ Ogg encoder improvements
+ ACM driver enumeration
+ Got test apps building again
+ Retired the JavaLayer port - its a few versions out of date anyway
+ WaveBuffer is now 16bit - experimental, needs optimising
+
+
+ 0.3.38.0
+ 7 May 2005
+ WaveStream::ReadInt16 optimisation
+ Fixed bugs in 16 bit positioning code
+ More XML documentation
+
+
+ 0.3.39.0
+ 10 May 2005
+ Initial Fader control implementation
+
+
+ 0.4.40.0
+ 12 May 2005
+ A very basic time domain convolution
+ Improvements to wave-reader and writer for floating point audio
+
+
+ 0.4.41.0
+ 25 May 2005
+ WaveChannel can supply from stereo input
+
+
+ 0.4.42.0
+ 27 May 2005
+ Initial VST interfaces and enums
+
+
+ 0.4.43.0
+ 3 Jun 2005
+ VstLoader implements IVstEffect
+ Began converting dispatcher opcodes to IVstEffect functions
+
+
+ 0.4.44.0
+ 6 Jun 2005
+ Finished IVstEffect functions
+
+
+ 0.4.45.0
+ 7 Jun 2005
+ IVstEffect function implementations for VstLoader
+
+
+ 0.4.46.0
+ 5 Aug 2005
+ Final consolidation of VST, prior to removal
+
+
+ 0.4.47.0
+ 27 Oct 2005
+ Wave Channel can convert mono to stereo now
+
+
+ 0.4.48.0
+ 27 Oct 2005
+ Wave Channel and Wave Mixer used for first time
+
+
+ 0.4.49.0
+ 28 Oct 2005
+ Volume and pan slider controls
+ Channel strip and WavePlayer export to WAV
+ WaveMixer doesn't go on indefinitely
+
+
+ 0.5.50.0
+ 31 Oct 2005
+ Some more LCD control characters
+ Initial WaveViewer control
+
+
+ 0.5.51.0
+ 3 Nov 2005
+ Dual channel strip in WavePlayer
+
+
+ 0.5.52.0
+ 4 Nov 2005
+ Fixed bad calculation of offset seconds in WavePlayer
+ Improved checking that we don't go beyond the end of streams
+
+
+ 0.5.53.0
+ 9 Nov 2005
+ SoundFont reading improvements for conversion to sfz
+
+
+ 0.5.54.0
+ 10 Nov 2005
+ IWavePlayer interface
+ Initial DirectSoundOut class
+ Major rework to return to 8 bit reads on all WaveStream, ready for inheriting Stream
+
+
+ 0.5.55.0
+ 11 Nov 2005
+ Cleaned up WaveFileReader
+ WaveOut is an IWavePlayer
+ WaveFormatStream
+ Ability to select between WaveOut and DirectSound
+ Initial playing back through DirectSound
+
+
+ 0.5.56.0
+ 11 Nov 2005
+ Retired StreamMixer project
+ WavePlayer better switching between settings
+ DirectSound feeds in on a timer now, (from MSDN Coding 4 Fun Drum Machine demo)
+
+
+ 0.5.57.0
+ 14 Nov 2005
+ DirectSoundOut fills buffer only twice per latency
+ DirectSoundOut stops at end
+ WavePlayer now has three channels
+ Selectable latency in WavePlayer
+
+
+ 0.5.58.0
+ 15 Nov 2005
+ DirectSoundOut now only reads buffers of the right size, which solves GSM cutout issues
+ WaveOut dispenses with an unnecessary delegate by passing WaveStream to WaveBuffer
+
+
+ 0.5.59.0
+ 16 Nov 2005
+ Fixed a crash in AdjustVolume on the MixerStream
+ sfz loop_continuous fix
+
+
+ 0.6.60.0
+ 16 Nov 2005
+ Converted to .NET 2.0
+ n.b. DirectSound has issues - needed to turn off the LoaderLock Managed Debug Assistant
+
+
+ 0.6.61.0
+ 17 Nov 2005
+ Made a 16 and 32 bit mixer stream
+
+
+ 0.6.62.0
+ 21 Nov 2005
+ Made a 32 bit WaveChannel stream
+ A 32 to 16 bit conversion stream
+ More MM error codes
+ 32 bit audio path tested and working
+
+
+ 0.6.63.0
+ 21 Nov 2005
+ Initial support for an ACM MP3 decoder - not working yet
+
+
+ 0.6.64.0
+ 22 Nov 2005
+ Basic working MP3 playback
+
+
+ 0.6.65.0
+ 25 Nov 2005
+ ADPCM Wave Format
+ Wave Formats can serialize themselves
+ WaveFileWriter can write non PCM formats
+
+
+ 0.6.66.0
+ 28 Nov 2005
+ WaveFileWriter writes a fact chunk - non-ideal though
+ Improved support for playback of compressed formats
+
+
+ 0.6.67.0
+ 28 Nov 2005
+ Improvements to BlockAlign and GetReadSize
+
+
+ 0.6.68.0
+ 1 Dec 2005
+ Nice ADPCM converter
+
+
+ 0.6.69.0
+ 5 Dec 2005
+ Support for AGC codec
+ Support for Speed codec
+
+
+ 0.7.70.0
+ 12 Dec 2005
+ WaveStream inherits Stream
+
+
+ 0.7.71.0
+ 19 Dec 2005
+ Minor bug fix to WaveMixer classes
+
+
+ 0.7.72.0
+ 3 Jan 2006
+ NICE specific code removed
+
+
+ 0.7.73.0
+ 6 Jan 2006
+ MP3 Reader can read ID3 tags and frames now
+
+
+ 0.7.74.0
+ 12 Jan 2006
+ Xing header support
+
+
+ 0.7.75.0
+ 15 Feb 2006
+ Reorganised class structures
+ WaveIn recording support added
+
+
+ 0.7.76.0
+ 15 Feb 2006
+ More structural reorganisation
+ Got rid of some compiler warnings
+ Retired 16 bit mixing code
+
+
+ 0.7.77.0
+ 16 Feb 2006
+ Improved WaveViewer control
+
+
+ 0.7.78.0
+ 17 Feb 2006
+ Fader control uses an image for the slider
+
+
+ 0.7.79.0
+ 20 Feb 2006
+ Added some copyright messages to SoundFont source files
+ Added BiQuad filters class
+
+
+ 0.8.80.0
+ 21 Feb 2006
+ Added envelope detector
+ Added simple compressor
+ Added simple gate
+
+
+ 0.8.81.0
+ 4 Apr 2006
+ ACM stream bug fixes
+ Support for waveOut window message callbacks
+ Wave In Recording bug fixes
+
+
+ 0.8.82.0
+ 13 Sep 2006
+ SimpleCompressor Wave Stream
+ Optimisation to WaveViewer
+ Minor bugfixes to Wave classes
+ Created a new Pot control
+
+
+ 0.8.83.0
+ 14 Sep 2006
+ Real-time adjustment of SimpleCompressor Wave Stream
+ Pot control drawing enhancements
+ The beginnings of a track-view control
+ The beginnings of a time-line control
+
+
+ 0.8.84.0
+ 21 Sep 2006
+ TimeLine control has a now cursor
+ TimeLine control can zoom
+
+
+ 0.8.85.0
+ 22 Sep 2006
+ TimeLine supports changing colours
+ TrackView can draw clips
+ New trackheader control
+
+
+ 0.8.86.0
+ 29 Sep 2006
+ MIDI events now support being exported
+ MIDI TrackSequenceNumber event
+ MIDI KeySignature event
+
+
+ 0.8.87.0
+ 2 Oct 2006
+ Bugfix for exporting note-off
+ Alternative constructors for MIDI events
+ Bugfix for exporting MIDI variable length integers
+ WaveFileReader can report information on non-standard chunks
+
+
+ 0.8.88.0
+ 4 Oct 2006
+ Bugfix MIDI export event sorting
+
+
+ 0.8.89.0
+ 5 Oct 2006
+ Bugfix MIDI export event sorting
+ Some support for modifying MIDI event parameters
+
+
+ 0.9.90.0
+ 6 Oct 2006
+ Bugfix Time Signature Event and Control Change Event
+ New SMPTE Offset event
+ Patch and Bank name meta events added
+ Meta events use VarInts for lengths now
+ Allow non-strict reading of MIDI file
+
+
+ 0.9.91.0
+ 9 Oct 2006
+ Minor updates to support EZdrummer MIDI converter
+ Beginnings of a new WaveOut class with its own thread
+
+
+ 0.9.92.0
+ 10 Oct 2006
+ Fixed a bug in WaveFileReader
+ Fix to ensure track-view shows correct length
+ An alternative thread-safe approach using locking
+ Initial ASIO classes created
+
+
+ 0.9.93.0
+ 11 Oct 2006
+ Support for exporting MIDI type 0 files
+ Can parse MIDI files with more than one end track marker per track
+
+
+ 0.9.94.0
+ 13 Oct 2006
+ Recognises some more rare MIDI meta event types
+ Initial support for reading Cakewalk drum map files
+
+
+ 0.9.95.0
+ 16 Oct 2006
+ MIDI events report channel from 1 to 16 now rather than 0 to 15
+ Got rid of the fader png image
+
+
+ 0.9.96.0
+ 17 Oct 2006
+ Cakewalk drum map enhancements
+
+
+ 0.9.97.0
+ 29 Oct 2006
+ ByteEncoding added
+ MIDI Text events use byte encoding for reading and writing
+
+
+ 0.9.97.1
+ 30 Oct 2006
+ ProgressLog control and AboutForm added
+ MIDI Text events can have their text modified
+
+
+ 0.9.97.2
+ 31 Oct 2006
+ ProgressLog control can report its text
+ Initial support for file association modification
+
+
+ 0.9.97.3
+ 1 Nov 2006
+ Bug fixes to file associations
+
+
+ 0.9.98.0
+ 6 Nov 2006
+ Support for modifying MIDI Control Change Event parameters
+
+
+ 0.9.98.1
+ 7 Nov 2006
+ After-touch pressure can be set
+ Note number and velocity can be set
+
+
+ 0.9.98.2
+ 9 Nov 2006
+ Pitch wheel event modifications
+
+
+ 0.9.98.3
+ 10 Nov 2006
+ Helper function for detecting note off events
+
+
+ 0.9.98.4
+ 1 Mar 2007
+ Updated some XML documentation
+
+
+ 0.9.99.0
+ 6 Mar 2007
+ Some checking for end of track markers in MIDI files
+
+
+ 1.0.100.0
+ 8 Mar 2007
+ WaveMixerStream32 updated ready to support dynamic adding of streams
+ Some bugfixes to WaveOut to support auto stop mode again
+
+
+ 1.0.101.0
+ 13 Mar 2007
+ Added some new NoteEvent and NoteOnEvent constructors
+
+
+ 1.0.102.0
+ 16 Mar 2007
+ Customisable about box URL, Email and Copyright
+
+
+ 1.0.103.0
+ 16 Mar 2007
+ Ability to remove WaveStreams from WaveMixerStream32
+
+
+ 1.0.104.0
+ 19 Mar 2007
+ WaveOffsetStream created
+ WaveStream32 preparation for 24 bit inputs
+ WaveStream32 new default constructor
+ Made the decibels to linear conversion functions public
+
+
+ 1.0.105.0
+ 30 Mar 2007
+ New constructor for ControlChangeEvent
+ New constructor for ChannelAfterTouchEvent
+ New constructor and property setting for PatchChangeEvent
+ New constructor for PitchWheelChangeEvent
+
+
+ 1.0.106.0
+ 2 Apr 2007
+ Bugfix for sysex event writing
+ MidiEvent IsEndTrack and IsNoteOff are now static functions
+ New IsNoteOn function
+ NoteOnEvent now updates the NoteNumber and Channel of its OffEvent when they are modified
+
+
+ 1.0.107.0
+ 3 Apr 2007
+ MIDI events are now sorted using a stable algorithm to allow
+ batch file processing utilities to retain original ordering of events
+ with the same start times.
+
+
+ 1.0.108.0
+ 4 Apr 2007
+ New MidiEventCollection class to make converting MIDI file types
+ more generic
+ Added an NUnit unit tests library
+ Fixed a bug in meta event constructor
+
+
+ 1.0.109.0
+ 5 Apr 2007
+ MidiFile updated to use MidiEventCollection
+
+
+ 1.0.109.1
+ 7 Apr 2007
+ Fixed a bug in MidiEventCollection.AddEvent
+
+
+ 1.0.110.0
+ 7 Apr 2007
+ Many enhancements to MIDI interop
+ New MidiIn, MidiInCapabilities classes
+ Added a new NAudioDemo for testing / demonstrating use of NAudio APIs
+
+
+ 1.0.111.0
+ 18 Apr 2007
+ More MidiEventCollection automated tests
+ Fixes to MidiEventCollection
+
+
+ 1.0.112.0
+ 19 Apr 2007
+ Some improvements to MIDI out interop
+ Test application can now send test MIDI out messages
+
+
+ 1.1.113.0
+ 26 Apr 2007
+ Allow invalid NoteOff velocities to be read from a MIDI file
+ MIDI File Splitter project moved off to its own CodePlex project
+
+
+ 1.1.114.0
+ 27 Mar 2008
+ Fixed a defect in WaveStream32 constructor
+ Restored the Managed DirectX output to the solution
+ Turned off ManagedLoaderLock to cope with DirectSound known issues (http://www.thezbuffer.com/articles/304.aspx)
+ Updated the NAudioDemo project to do some audio playback
+ Added Rob Philpot's managed ASIO wrapper (http://www.codeproject.com/KB/mcpp/Asio.Net.aspx)
+
+
+ 1.1.115.0
+ 17 May 2008
+ Updated to latest version of Rob Philpot's managed ASIO wrapper (http://www.codeproject.com/KB/mcpp/Asio.Net.aspx)
+
+
+ 1.1.116.0
+ 19 May 2008
+ Made some very small beginnings to WASAPI support
+ Added a couple of unit tests for AcmDriver
+
+
+ 1.1.117.0
+ 20 May 2008
+ Added some WASAPI interfaces (mainly MMDevice API)
+
+
+ 1.1.118.0
+ 24 May 2008
+ Added WASAPI interface IMMEndpoint
+ begun MMDevice implementation
+ Decided to make use of some code from Ray Molenkamp to speed WASAPI implementation
+
+
+ 1.1.119.0
+ 26 May 2008
+ Borrowing lots more from Ray Molenkamp
+ Renamed WASAPI namespace to CoreAudioApi
+
+
+ 1.2.120.0
+ 27 May 2008
+ More work on WASAPI interfaces (IAudioClient, IAudioRenderClient)
+
+
+ 1.2.121.0
+ 28 May 2008
+ WASAPI interface - IAudioCaptureClient
+
+
+ 1.2.122.0
+ 28 May 2008
+ More WASAPI work - AudioClient, WaveFormatExtensible, AudioRenderClient
+
+
+ 1.2.123.0
+ 29 May 2008
+ AudioClient IsFormatSupported - it appears WASAPI does not do sample rate conversion for you
+
+
+ 1.2.124.0
+ 29 May 2008
+ Begun work on WasapiOut
+ Removed Resume from IWavePlayer
+ Removed Pan from IWavePlayer
+ PlaybackState instead of IsPlaying and IsPaused on IWavePlayer
+ WaveOut implementation simplified
+
+
+ 1.2.125.0
+ 30 May 2008
+ AcmDriver enhancements
+ WMA Wave Format
+
+
+ 1.2.126.0
+ 31 May 2008
+ WASAPI out working! but need a solution for SRC
+
+
+ 1.2.127.0
+ 31 May 2008
+ Lots of DMO interop written (DirectX media objects)
+
+
+ 1.2.128.0
+ 1 June 2008
+ Lots more DMO interop - Resampler DMO
+ Media Object can set input and output WaveFormat
+ Getting caught back up with XML documentation
+ Media Object can get input and output buffer sizes
+
+
+
+ 1.2.129.0
+ 2 June 2008
+ Eventually got IMediaBuffer ProcessInput working (thanks to PreserveSig attribute)
+ IMediaBuffer ProcessOutput also working, but Resampler not outputting expected number of bytes
+ ResamplerDmoStream created
+
+
+ 1.2.130.0
+ 3 June 2008
+ ResamplerDmoStream bugfixes
+
+
+ 1.2.131.0
+ 10 June 2008
+ Wasapi output stream uses ResamplerDmoStream (but needs app to be MTAThread to work atm)
+ Audio Client using PreserveSig
+ More reliable Wasapi out
+ NAudio demo plays a file now rather than mixing a whole folder together
+
+
+
+ 1.2.132.0
+ 11 June 2008
+ Mark Heath
+ Added Alexandre Mutel's Native DirectSound class
+ Added a bug fix to WasapiOut PlayThread (thanks Alexandre)
+
+
+ 1.2.133.0
+ Alexandre Mutel
+ WASAPI Out fixes
+
+
+ 1.2.134.0
+ Mark Heath
+ Fix to get WASAPI out working with DMO resampler again
+ Fix to get WASAPI working with DMO resampler without needing MTA Thread (woohoo!)
+ Improvements to NAudioDemo Audio Playback Form
+
+
+ 1.2.135.0
+ Mark Heath
+ 13 Jun 2008
+ More unit tests for AudioClient
+ Added option for WASAPI exclusive mode to NAudio Demo (not working yet)
+ NAudioDemo can play MP3 files (not at low latency though)
+ Position bar in NAudioDemo
+ Performance enhancements to WaveChannel32
+ MP3 playback less choppy
+
+
+ 1.2.136.0
+ Mark Heath
+ 14 Jun 2008
+ WASAPI Exclusive mode working (thanks Alexandre)
+ Proper closing of WASAPI allowing us immediate re-open
+ NAudioDemo has some ACM encoding demos (mu-law, a-law working so far)
+ Added interop for AcmFormatEnum
+ Added interop for AcmFormatTagEnum
+ AudioDriver can now enumerate all tags and formats
+
+
+ 1.2.137.0
+ Mark Heath
+ 15 Jun 2008
+ Alexandre Mutel added WASAPI event model playback
+ NAudioDemo has better selection of output driver settings
+ Added interop for AcmFormatChoose
+ Moved ACM interop into NAudio.Wave.Compression namespace
+
+
+ 1.2.138.0
+ Mark Heath
+ 16 Jun 2008
+ Added a Custom Marshaller for WaveFormat (not used everywhere yet)
+ Fixed some marshalling bugs in ResamplerDMOStream caused by WaveFileReader using WaveFormatExtraData
+ Got majority of unit tests working (things had slipped a little!)
+
+
+ 1.2.139.0
+ Mark Heath
+ 18 Jun 2008
+ NativeDirectSound working in STAThread mode
+ NAudioDemo stops playback at end of file
+ Unit Test reorganization
+
+
+ 1.2.140.0
+ Mark Heath
+ 19 Jun 2008
+ WaveStream32 has option not to pad reads out to full length
+ NAudioDemo sets volume on WaveStream rather than WaveOut
+
+
+ 1.2.141.0
+ Alexandre Mutel
+ 22 Jun 2008
+ Managed ASIO Output implementation
+
+
+ 1.2.142.0
+ Mark Heath
+ 22 Jun 2008
+ NAudioDemo has basic event-driven recording demo
+
+
+ 1.2.143.0
+ Mark Heath
+ 23 Jun 2008
+ Fix to MP3 smooth playback
+ A circular buffer class
+ A block alignment reduction stream
+ Xing header detection without throwing exceptions
+
+
+ 1.2.144.0
+ Mark Heath
+ 26 June 2008
+ Improved error handling on NAudioDemo
+
+
+ 1.3.1.0
+ Mark Heath
+ 27 June 2008
+ IWavePlayer using IWaveProvider and WaveBuffer (initial port)
+
+
+ 1.3.2.0
+ Mark Heath
+ 28 June 2008
+ NativeDirectSoundOut now becomes DirectSoundOut as Managed Direct X is retired
+
+
+ 1.3.3.0
+ Mark Heath
+ 20 Oct 2008
+ Some possible bugfixes to ASIO
+ MeteringStream
+ VolumeMeter
+ WaveFormPainter
+
+
+ 1.3.4.0
+ Mark Heath
+ 12 Jan 2009
+ Can write larger Var Ints in MIDI files
+
+
+ 1.3.5.0
+ Mark Heath
+ 19 Sep 2009
+ Many changes not listed here, see checkin history
+ WaveIn callback options brought into line with WaveOut
+
+
+ 1.3.11.0
+ Mark Heath
+ 30 Aug 2010
+ Many changes not listed here, see checkin history
+ Sequencer-specific event added
+
+
+ 1.3.12.0
+ Mark Heath
+ 29 Sep 2010
+ Changes not listed here, see checkin history
+ Added BufferedWaveProvider
+ Added Wave16ToIeeeProvider
+ Added WaveInProvider
+
+
+ 1.3.13.0
+ Mark Heath
+ 14 Oct 2010
+ Retired WaveStream.GetReadSize - not reliable, too difficult to implement, hardly used
+ Cleanup on MP3 File Reader
+
+
+ 1.3.14.0
+ Mark Heath
+ 7 Nov 2010
+ Huge improvements to MP3 File Reader
+
+
diff --git a/NAudio/Codecs/ALawDecoder.cs b/NAudio/Codecs/ALawDecoder.cs
new file mode 100644
index 00000000..8cf4ff22
--- /dev/null
+++ b/NAudio/Codecs/ALawDecoder.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Codecs
+{
+ ///
+ /// a-law decoder
+ /// based on code from:
+ /// http://hazelware.luggle.com/tutorials/mulawcompression.html
+ ///
+ public class ALawDecoder
+ {
+ ///
+ /// only 512 bytes required, so just use a lookup
+ ///
+ private static readonly short[] ALawDecompressTable = new short[256]
+ {
+ -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
+ -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
+ -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
+ -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392,
+ -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944,
+ -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136,
+ -11008,-10496,-12032,-11520,-8960, -8448, -9984, -9472,
+ -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568,
+ -344, -328, -376, -360, -280, -264, -312, -296,
+ -472, -456, -504, -488, -408, -392, -440, -424,
+ -88, -72, -120, -104, -24, -8, -56, -40,
+ -216, -200, -248, -232, -152, -136, -184, -168,
+ -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184,
+ -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696,
+ -688, -656, -752, -720, -560, -528, -624, -592,
+ -944, -912, -1008, -976, -816, -784, -880, -848,
+ 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736,
+ 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784,
+ 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368,
+ 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392,
+ 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944,
+ 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136,
+ 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472,
+ 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568,
+ 344, 328, 376, 360, 280, 264, 312, 296,
+ 472, 456, 504, 488, 408, 392, 440, 424,
+ 88, 72, 120, 104, 24, 8, 56, 40,
+ 216, 200, 248, 232, 152, 136, 184, 168,
+ 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184,
+ 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696,
+ 688, 656, 752, 720, 560, 528, 624, 592,
+ 944, 912, 1008, 976, 816, 784, 880, 848
+ };
+
+ ///
+ /// Converts an a-law encoded byte to a 16 bit linear sample
+ ///
+ /// a-law encoded byte
+ /// Linear sample
+ public static short ALawToLinearSample(byte aLaw)
+ {
+ return ALawDecompressTable[aLaw];
+ }
+ }
+}
diff --git a/NAudio/Codecs/ALawEncoder.cs b/NAudio/Codecs/ALawEncoder.cs
new file mode 100644
index 00000000..cd953ed7
--- /dev/null
+++ b/NAudio/Codecs/ALawEncoder.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Codecs
+{
+ ///
+ /// A-law encoder
+ ///
+ public static class ALawEncoder
+ {
+ private const int cBias = 0x84;
+ private const int cClip = 32635;
+ private static readonly byte[] ALawCompressTable = new byte[128]
+ {
+ 1,1,2,2,3,3,3,3,
+ 4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7
+ };
+
+ ///
+ /// Encodes a single 16 bit sample to a-law
+ ///
+ /// 16 bit PCM sample
+ /// a-law encoded byte
+ public static byte LinearToALawSample(short sample)
+ {
+ int sign;
+ int exponent;
+ int mantissa;
+ byte compressedByte;
+
+ sign = ((~sample) >> 8) & 0x80;
+ if (sign == 0)
+ sample = (short)-sample;
+ if (sample > cClip)
+ sample = cClip;
+ if (sample >= 256)
+ {
+ exponent = (int)ALawCompressTable[(sample >> 8) & 0x7F];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ compressedByte = (byte)((exponent << 4) | mantissa);
+ }
+ else
+ {
+ compressedByte = (byte)(sample >> 4);
+ }
+ compressedByte ^= (byte)(sign ^ 0x55);
+ return compressedByte;
+ }
+ }
+}
diff --git a/NAudio/Codecs/G722Codec.cs b/NAudio/Codecs/G722Codec.cs
new file mode 100644
index 00000000..3b48dec6
--- /dev/null
+++ b/NAudio/Codecs/G722Codec.cs
@@ -0,0 +1,628 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Codecs
+{
+ ///
+ /// SpanDSP - a series of DSP components for telephony
+ ///
+ /// g722_decode.c - The ITU G.722 codec, decode part.
+ ///
+ /// Written by Steve Underwood <steveu@coppice.org>
+ ///
+ /// Copyright (C) 2005 Steve Underwood
+ /// Ported to C# by Mark Heath 2011
+ ///
+ /// Despite my general liking of the GPL, I place my own contributions
+ /// to this code in the public domain for the benefit of all mankind -
+ /// even the slimy ones who might try to proprietize my work and use it
+ /// to my detriment.
+ ///
+ /// Based in part on a single channel G.722 codec which is:
+ /// Copyright (c) CMU 1993
+ /// Computer Science, Speech Group
+ /// Chengxiang Lu and Alex Hauptmann
+ ///
+ public class G722Codec
+ {
+ ///
+ /// hard limits to 16 bit samples
+ ///
+ static short Saturate(int amp)
+ {
+ short amp16;
+
+ // Hopefully this is optimised for the common case - not clipping
+ amp16 = (short)amp;
+ if (amp == amp16)
+ return amp16;
+ if (amp > Int16.MaxValue)
+ return Int16.MaxValue;
+ return Int16.MinValue;
+ }
+
+ static void Block4(G722CodecState s, int band, int d)
+ {
+ int wd1;
+ int wd2;
+ int wd3;
+ int i;
+
+ // Block 4, RECONS
+ s.Band[band].d[0] = d;
+ s.Band[band].r[0] = Saturate(s.Band[band].s + d);
+
+ // Block 4, PARREC
+ s.Band[band].p[0] = Saturate(s.Band[band].sz + d);
+
+ // Block 4, UPPOL2
+ for (i = 0; i < 3; i++)
+ s.Band[band].sg[i] = s.Band[band].p[i] >> 15;
+ wd1 = Saturate(s.Band[band].a[1] << 2);
+
+ wd2 = (s.Band[band].sg[0] == s.Band[band].sg[1]) ? -wd1 : wd1;
+ if (wd2 > 32767)
+ wd2 = 32767;
+ wd3 = (s.Band[band].sg[0] == s.Band[band].sg[2]) ? 128 : -128;
+ wd3 += (wd2 >> 7);
+ wd3 += (s.Band[band].a[2] * 32512) >> 15;
+ if (wd3 > 12288)
+ wd3 = 12288;
+ else if (wd3 < -12288)
+ wd3 = -12288;
+ s.Band[band].ap[2] = wd3;
+
+ // Block 4, UPPOL1
+ s.Band[band].sg[0] = s.Band[band].p[0] >> 15;
+ s.Band[band].sg[1] = s.Band[band].p[1] >> 15;
+ wd1 = (s.Band[band].sg[0] == s.Band[band].sg[1]) ? 192 : -192;
+ wd2 = (s.Band[band].a[1] * 32640) >> 15;
+
+ s.Band[band].ap[1] = Saturate(wd1 + wd2);
+ wd3 = Saturate(15360 - s.Band[band].ap[2]);
+ if (s.Band[band].ap[1] > wd3)
+ s.Band[band].ap[1] = wd3;
+ else if (s.Band[band].ap[1] < -wd3)
+ s.Band[band].ap[1] = -wd3;
+
+ // Block 4, UPZERO
+ wd1 = (d == 0) ? 0 : 128;
+ s.Band[band].sg[0] = d >> 15;
+ for (i = 1; i < 7; i++)
+ {
+ s.Band[band].sg[i] = s.Band[band].d[i] >> 15;
+ wd2 = (s.Band[band].sg[i] == s.Band[band].sg[0]) ? wd1 : -wd1;
+ wd3 = (s.Band[band].b[i] * 32640) >> 15;
+ s.Band[band].bp[i] = Saturate(wd2 + wd3);
+ }
+
+ // Block 4, DELAYA
+ for (i = 6; i > 0; i--)
+ {
+ s.Band[band].d[i] = s.Band[band].d[i - 1];
+ s.Band[band].b[i] = s.Band[band].bp[i];
+ }
+
+ for (i = 2; i > 0; i--)
+ {
+ s.Band[band].r[i] = s.Band[band].r[i - 1];
+ s.Band[band].p[i] = s.Band[band].p[i - 1];
+ s.Band[band].a[i] = s.Band[band].ap[i];
+ }
+
+ // Block 4, FILTEP
+ wd1 = Saturate(s.Band[band].r[1] + s.Band[band].r[1]);
+ wd1 = (s.Band[band].a[1] * wd1) >> 15;
+ wd2 = Saturate(s.Band[band].r[2] + s.Band[band].r[2]);
+ wd2 = (s.Band[band].a[2] * wd2) >> 15;
+ s.Band[band].sp = Saturate(wd1 + wd2);
+
+ // Block 4, FILTEZ
+ s.Band[band].sz = 0;
+ for (i = 6; i > 0; i--)
+ {
+ wd1 = Saturate(s.Band[band].d[i] + s.Band[band].d[i]);
+ s.Band[band].sz += (s.Band[band].b[i] * wd1) >> 15;
+ }
+ s.Band[band].sz = Saturate(s.Band[band].sz);
+
+ // Block 4, PREDIC
+ s.Band[band].s = Saturate(s.Band[band].sp + s.Band[band].sz);
+ }
+
+ static readonly int[] wl = { -60, -30, 58, 172, 334, 538, 1198, 3042 };
+ static readonly int[] rl42 = { 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, 2, 1, 0 };
+ static readonly int[] ilb = { 2048, 2093, 2139, 2186, 2233, 2282, 2332, 2383, 2435, 2489, 2543, 2599, 2656, 2714, 2774, 2834, 2896, 2960, 3025, 3091, 3158, 3228, 3298, 3371, 3444, 3520, 3597, 3676, 3756, 3838, 3922, 4008 };
+ static readonly int[] wh = { 0, -214, 798 };
+ static readonly int[] rh2 = { 2, 1, 2, 1 };
+ static readonly int[] qm2 = { -7408, -1616, 7408, 1616 };
+ static readonly int[] qm4 = { 0, -20456, -12896, -8968, -6288, -4240, -2584, -1200, 20456, 12896, 8968, 6288, 4240, 2584, 1200, 0 };
+ static readonly int[] qm5 = { -280, -280, -23352, -17560, -14120, -11664, -9752, -8184, -6864, -5712, -4696, -3784, -2960, -2208, -1520, -880, 23352, 17560, 14120, 11664, 9752, 8184, 6864, 5712, 4696, 3784, 2960, 2208, 1520, 880, 280, -280 };
+ static readonly int[] qm6 = { -136, -136, -136, -136, -24808, -21904, -19008, -16704, -14984, -13512, -12280, -11192, -10232, -9360, -8576, -7856, -7192, -6576, -6000, -5456, -4944, -4464, -4008, -3576, -3168, -2776, -2400, -2032, -1688, -1360, -1040, -728, 24808, 21904, 19008, 16704, 14984, 13512, 12280, 11192, 10232, 9360, 8576, 7856, 7192, 6576, 6000, 5456, 4944, 4464, 4008, 3576, 3168, 2776, 2400, 2032, 1688, 1360, 1040, 728, 432, 136, -432, -136 };
+ static readonly int[] qmf_coeffs = { 3, -11, 12, 32, -210, 951, 3876, -805, 362, -156, 53, -11, };
+ static readonly int[] q6 = { 0, 35, 72, 110, 150, 190, 233, 276, 323, 370, 422, 473, 530, 587, 650, 714, 786, 858, 940, 1023, 1121, 1219, 1339, 1458, 1612, 1765, 1980, 2195, 2557, 2919, 0, 0 };
+ static readonly int[] iln = { 0, 63, 62, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 0 };
+ static readonly int[] ilp = { 0, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 0 };
+ static readonly int[] ihn = { 0, 1, 0 };
+ static readonly int[] ihp = { 0, 3, 2 };
+
+ ///
+ /// Decodes a buffer of G722
+ ///
+ /// Codec state
+ /// Output buffer (to contain decompressed PCM samples)
+ ///
+ /// Number of bytes in input G722 data to decode
+ /// Number of samples written into output buffer
+ public int Decode(G722CodecState state, short[] outputBuffer, byte[] inputG722Data, int inputLength)
+ {
+ int dlowt;
+ int rlow;
+ int ihigh;
+ int dhigh;
+ int rhigh;
+ int xout1;
+ int xout2;
+ int wd1;
+ int wd2;
+ int wd3;
+ int code;
+ int outlen;
+ int i;
+ int j;
+
+ outlen = 0;
+ rhigh = 0;
+ for (j = 0; j < inputLength; )
+ {
+ if (state.Packed)
+ {
+ // Unpack the code bits
+ if (state.InBits < state.BitsPerSample)
+ {
+ state.InBuffer |= (uint)(inputG722Data[j++] << state.InBits);
+ state.InBits += 8;
+ }
+ code = (int)state.InBuffer & ((1 << state.BitsPerSample) - 1);
+ state.InBuffer >>= state.BitsPerSample;
+ state.InBits -= state.BitsPerSample;
+ }
+ else
+ {
+ code = inputG722Data[j++];
+ }
+
+ switch (state.BitsPerSample)
+ {
+ default:
+ case 8:
+ wd1 = code & 0x3F;
+ ihigh = (code >> 6) & 0x03;
+ wd2 = qm6[wd1];
+ wd1 >>= 2;
+ break;
+ case 7:
+ wd1 = code & 0x1F;
+ ihigh = (code >> 5) & 0x03;
+ wd2 = qm5[wd1];
+ wd1 >>= 1;
+ break;
+ case 6:
+ wd1 = code & 0x0F;
+ ihigh = (code >> 4) & 0x03;
+ wd2 = qm4[wd1];
+ break;
+ }
+
+ // Block 5L, LOW BAND INVQBL
+ wd2 = (state.Band[0].det * wd2) >> 15;
+
+ // Block 5L, RECONS
+ rlow = state.Band[0].s + wd2;
+
+ // Block 6L, LIMIT
+ if (rlow > 16383)
+ rlow = 16383;
+ else if (rlow < -16384)
+ rlow = -16384;
+
+ // Block 2L, INVQAL
+ wd2 = qm4[wd1];
+ dlowt = (state.Band[0].det * wd2) >> 15;
+
+ // Block 3L, LOGSCL
+ wd2 = rl42[wd1];
+ wd1 = (state.Band[0].nb * 127) >> 7;
+ wd1 += wl[wd2];
+ if (wd1 < 0)
+ wd1 = 0;
+ else if (wd1 > 18432)
+ wd1 = 18432;
+ state.Band[0].nb = wd1;
+
+ // Block 3L, SCALEL
+ wd1 = (state.Band[0].nb >> 6) & 31;
+ wd2 = 8 - (state.Band[0].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ state.Band[0].det = wd3 << 2;
+
+ Block4(state, 0, dlowt);
+
+ if (!state.EncodeFrom8000Hz)
+ {
+ // Block 2H, INVQAH
+ wd2 = qm2[ihigh];
+ dhigh = (state.Band[1].det * wd2) >> 15;
+
+ // Block 5H, RECONS
+ rhigh = dhigh + state.Band[1].s;
+
+ // Block 6H, LIMIT
+ if (rhigh > 16383)
+ rhigh = 16383;
+ else if (rhigh < -16384)
+ rhigh = -16384;
+
+ // Block 2H, INVQAH
+ wd2 = rh2[ihigh];
+ wd1 = (state.Band[1].nb * 127) >> 7;
+ wd1 += wh[wd2];
+ if (wd1 < 0)
+ wd1 = 0;
+ else if (wd1 > 22528)
+ wd1 = 22528;
+ state.Band[1].nb = wd1;
+
+ // Block 3H, SCALEH
+ wd1 = (state.Band[1].nb >> 6) & 31;
+ wd2 = 10 - (state.Band[1].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ state.Band[1].det = wd3 << 2;
+
+ Block4(state, 1, dhigh);
+ }
+
+ if (state.ItuTestMode)
+ {
+ outputBuffer[outlen++] = (short)(rlow << 1);
+ outputBuffer[outlen++] = (short)(rhigh << 1);
+ }
+ else
+ {
+ if (state.EncodeFrom8000Hz)
+ {
+ outputBuffer[outlen++] = (short)(rlow << 1);
+ }
+ else
+ {
+ // Apply the receive QMF
+ for (i = 0; i < 22; i++)
+ state.QmfSignalHistory[i] = state.QmfSignalHistory[i + 2];
+ state.QmfSignalHistory[22] = rlow + rhigh;
+ state.QmfSignalHistory[23] = rlow - rhigh;
+
+ xout1 = 0;
+ xout2 = 0;
+ for (i = 0; i < 12; i++)
+ {
+ xout2 += state.QmfSignalHistory[2 * i] * qmf_coeffs[i];
+ xout1 += state.QmfSignalHistory[2 * i + 1] * qmf_coeffs[11 - i];
+ }
+ outputBuffer[outlen++] = (short)(xout1 >> 11);
+ outputBuffer[outlen++] = (short)(xout2 >> 11);
+ }
+ }
+ }
+ return outlen;
+ }
+
+ ///
+ /// Encodes a buffer of G722
+ ///
+ /// Codec state
+ /// Output buffer (to contain encoded G722)
+ /// PCM 16 bit samples to encode
+ /// Number of samples in the input buffer to encode
+ /// Number of encoded bytes written into output buffer
+ public int Encode(G722CodecState state, byte[] outputBuffer, short[] inputBuffer, int inputBufferCount)
+ {
+ int dlow;
+ int dhigh;
+ int el;
+ int wd;
+ int wd1;
+ int ril;
+ int wd2;
+ int il4;
+ int ih2;
+ int wd3;
+ int eh;
+ int mih;
+ int i;
+ int j;
+ // Low and high band PCM from the QMF
+ int xlow;
+ int xhigh;
+ int g722_bytes;
+ // Even and odd tap accumulators
+ int sumeven;
+ int sumodd;
+ int ihigh;
+ int ilow;
+ int code;
+
+ g722_bytes = 0;
+ xhigh = 0;
+ for (j = 0; j < inputBufferCount; )
+ {
+ if (state.ItuTestMode)
+ {
+ xlow =
+ xhigh = inputBuffer[j++] >> 1;
+ }
+ else
+ {
+ if (state.EncodeFrom8000Hz)
+ {
+ xlow = inputBuffer[j++] >> 1;
+ }
+ else
+ {
+ // Apply the transmit QMF
+ // Shuffle the buffer down
+ for (i = 0; i < 22; i++)
+ state.QmfSignalHistory[i] = state.QmfSignalHistory[i + 2];
+ state.QmfSignalHistory[22] = inputBuffer[j++];
+ state.QmfSignalHistory[23] = inputBuffer[j++];
+
+ // Discard every other QMF output
+ sumeven = 0;
+ sumodd = 0;
+ for (i = 0; i < 12; i++)
+ {
+ sumodd += state.QmfSignalHistory[2 * i] * qmf_coeffs[i];
+ sumeven += state.QmfSignalHistory[2 * i + 1] * qmf_coeffs[11 - i];
+ }
+ xlow = (sumeven + sumodd) >> 14;
+ xhigh = (sumeven - sumodd) >> 14;
+ }
+ }
+ // Block 1L, SUBTRA
+ el = Saturate(xlow - state.Band[0].s);
+
+ // Block 1L, QUANTL
+ wd = (el >= 0) ? el : -(el + 1);
+
+ for (i = 1; i < 30; i++)
+ {
+ wd1 = (q6[i] * state.Band[0].det) >> 12;
+ if (wd < wd1)
+ break;
+ }
+ ilow = (el < 0) ? iln[i] : ilp[i];
+
+ // Block 2L, INVQAL
+ ril = ilow >> 2;
+ wd2 = qm4[ril];
+ dlow = (state.Band[0].det * wd2) >> 15;
+
+ // Block 3L, LOGSCL
+ il4 = rl42[ril];
+ wd = (state.Band[0].nb * 127) >> 7;
+ state.Band[0].nb = wd + wl[il4];
+ if (state.Band[0].nb < 0)
+ state.Band[0].nb = 0;
+ else if (state.Band[0].nb > 18432)
+ state.Band[0].nb = 18432;
+
+ // Block 3L, SCALEL
+ wd1 = (state.Band[0].nb >> 6) & 31;
+ wd2 = 8 - (state.Band[0].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ state.Band[0].det = wd3 << 2;
+
+ Block4(state, 0, dlow);
+
+ if (state.EncodeFrom8000Hz)
+ {
+ // Just leave the high bits as zero
+ code = (0xC0 | ilow) >> (8 - state.BitsPerSample);
+ }
+ else
+ {
+ // Block 1H, SUBTRA
+ eh = Saturate(xhigh - state.Band[1].s);
+
+ // Block 1H, QUANTH
+ wd = (eh >= 0) ? eh : -(eh + 1);
+ wd1 = (564 * state.Band[1].det) >> 12;
+ mih = (wd >= wd1) ? 2 : 1;
+ ihigh = (eh < 0) ? ihn[mih] : ihp[mih];
+
+ // Block 2H, INVQAH
+ wd2 = qm2[ihigh];
+ dhigh = (state.Band[1].det * wd2) >> 15;
+
+ // Block 3H, LOGSCH
+ ih2 = rh2[ihigh];
+ wd = (state.Band[1].nb * 127) >> 7;
+ state.Band[1].nb = wd + wh[ih2];
+ if (state.Band[1].nb < 0)
+ state.Band[1].nb = 0;
+ else if (state.Band[1].nb > 22528)
+ state.Band[1].nb = 22528;
+
+ // Block 3H, SCALEH
+ wd1 = (state.Band[1].nb >> 6) & 31;
+ wd2 = 10 - (state.Band[1].nb >> 11);
+ wd3 = (wd2 < 0) ? (ilb[wd1] << -wd2) : (ilb[wd1] >> wd2);
+ state.Band[1].det = wd3 << 2;
+
+ Block4(state, 1, dhigh);
+ code = ((ihigh << 6) | ilow) >> (8 - state.BitsPerSample);
+ }
+
+ if (state.Packed)
+ {
+ // Pack the code bits
+ state.OutBuffer |= (uint)(code << state.OutBits);
+ state.OutBits += state.BitsPerSample;
+ if (state.OutBits >= 8)
+ {
+ outputBuffer[g722_bytes++] = (byte)(state.OutBuffer & 0xFF);
+ state.OutBits -= 8;
+ state.OutBuffer >>= 8;
+ }
+ }
+ else
+ {
+ outputBuffer[g722_bytes++] = (byte)code;
+ }
+ }
+ return g722_bytes;
+ }
+ }
+
+ ///
+ /// Stores state to be used between calls to Encode or Decode
+ ///
+ public class G722CodecState
+ {
+ ///
+ /// ITU Test Mode
+ /// TRUE if the operating in the special ITU test mode, with the band split filters disabled.
+ ///
+ public bool ItuTestMode { get; set; }
+
+ ///
+ /// TRUE if the G.722 data is packed
+ ///
+ public bool Packed { get; private set; }
+
+ ///
+ /// 8kHz Sampling
+ /// TRUE if encode from 8k samples/second
+ ///
+ public bool EncodeFrom8000Hz { get; private set; }
+
+ ///
+ /// Bits Per Sample
+ /// 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps.
+ ///
+ public int BitsPerSample { get; private set; }
+
+ ///
+ /// Signal history for the QMF (x)
+ ///
+ public int[] QmfSignalHistory { get; private set; }
+
+ ///
+ /// Band
+ ///
+ public Band[] Band { get; private set; }
+
+ ///
+ /// In bit buffer
+ ///
+ public uint InBuffer { get; internal set; }
+
+ ///
+ /// Number of bits in InBuffer
+ ///
+ public int InBits { get; internal set; }
+
+ ///
+ /// Out bit buffer
+ ///
+ public uint OutBuffer { get; internal set; }
+
+ ///
+ /// Number of bits in OutBuffer
+ ///
+ public int OutBits { get; internal set; }
+
+ ///
+ /// Creates a new instance of G722 Codec State for a
+ /// new encode or decode session
+ ///
+ /// Bitrate (typically 64000)
+ /// Special options
+ public G722CodecState(int rate, G722Flags options)
+ {
+ this.Band = new Band[2] { new Band(), new Band() };
+ this.QmfSignalHistory = new int[24];
+ this.ItuTestMode = false;
+
+ if (rate == 48000)
+ this.BitsPerSample = 6;
+ else if (rate == 56000)
+ this.BitsPerSample = 7;
+ else if (rate == 64000)
+ this.BitsPerSample = 8;
+ else
+ throw new ArgumentException("Invalid rate, should be 48000, 56000 or 64000");
+ if ((options & G722Flags.SampleRate8000) == G722Flags.SampleRate8000)
+ this.EncodeFrom8000Hz = true;
+ if (((options & G722Flags.Packed) == G722Flags.Packed) && this.BitsPerSample != 8)
+ this.Packed = true;
+ else
+ this.Packed = false;
+ this.Band[0].det = 32;
+ this.Band[1].det = 8;
+ }
+ }
+
+ ///
+ /// Band data for G722 Codec
+ ///
+ public class Band
+ {
+ /// s
+ public int s;
+ /// sp
+ public int sp;
+ /// sz
+ public int sz;
+ /// r
+ public int[] r = new int[3];
+ /// a
+ public int[] a = new int[3];
+ /// ap
+ public int[] ap = new int[3];
+ /// p
+ public int[] p = new int[3];
+ /// d
+ public int[] d = new int[7];
+ /// b
+ public int[] b = new int[7];
+ /// bp
+ public int[] bp = new int[7];
+ /// sg
+ public int[] sg = new int[7];
+ /// nb
+ public int nb;
+ /// det
+ public int det;
+ }
+
+ ///
+ /// G722 Flags
+ ///
+ [Flags]
+ public enum G722Flags
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// Using a G722 sample rate of 8000
+ ///
+ SampleRate8000 = 0x0001,
+ ///
+ /// Packed
+ ///
+ Packed = 0x0002
+ }
+}
diff --git a/NAudio/Codecs/MuLawDecoder.cs b/NAudio/Codecs/MuLawDecoder.cs
new file mode 100644
index 00000000..b29755b2
--- /dev/null
+++ b/NAudio/Codecs/MuLawDecoder.cs
@@ -0,0 +1,60 @@
+namespace NAudio.Codecs
+{
+ ///
+ /// mu-law decoder
+ /// based on code from:
+ /// http://hazelware.luggle.com/tutorials/mulawcompression.html
+ ///
+ public static class MuLawDecoder
+ {
+ ///
+ /// only 512 bytes required, so just use a lookup
+ ///
+ private static readonly short[] MuLawDecompressTable = new short[256]
+ {
+ -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
+ -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
+ -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
+ -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
+ -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
+ -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
+ -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
+ -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
+ -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
+ -1372, -1308, -1244, -1180, -1116, -1052, -988, -924,
+ -876, -844, -812, -780, -748, -716, -684, -652,
+ -620, -588, -556, -524, -492, -460, -428, -396,
+ -372, -356, -340, -324, -308, -292, -276, -260,
+ -244, -228, -212, -196, -180, -164, -148, -132,
+ -120, -112, -104, -96, -88, -80, -72, -64,
+ -56, -48, -40, -32, -24, -16, -8, -1,
+ 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
+ 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
+ 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
+ 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316,
+ 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140,
+ 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092,
+ 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004,
+ 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980,
+ 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436,
+ 1372, 1308, 1244, 1180, 1116, 1052, 988, 924,
+ 876, 844, 812, 780, 748, 716, 684, 652,
+ 620, 588, 556, 524, 492, 460, 428, 396,
+ 372, 356, 340, 324, 308, 292, 276, 260,
+ 244, 228, 212, 196, 180, 164, 148, 132,
+ 120, 112, 104, 96, 88, 80, 72, 64,
+ 56, 48, 40, 32, 24, 16, 8, 0
+ };
+
+ ///
+ /// Converts a mu-law encoded byte to a 16 bit linear sample
+ ///
+ /// mu-law encoded byte
+ /// Linear sample
+ public static short MuLawToLinearSample(byte muLaw)
+ {
+ return MuLawDecompressTable[muLaw];
+ }
+
+ }
+}
diff --git a/NAudio/Codecs/MuLawEncoder.cs b/NAudio/Codecs/MuLawEncoder.cs
new file mode 100644
index 00000000..8b04954c
--- /dev/null
+++ b/NAudio/Codecs/MuLawEncoder.cs
@@ -0,0 +1,53 @@
+namespace NAudio.Codecs
+{
+ ///
+ /// mu-law encoder
+ /// based on code from:
+ /// http://hazelware.luggle.com/tutorials/mulawcompression.html
+ ///
+ public static class MuLawEncoder
+ {
+ private const int cBias = 0x84;
+ private const int cClip = 32635;
+
+ private static readonly byte[] MuLawCompressTable = new byte[256]
+ {
+ 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+ 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
+ };
+
+ ///
+ /// Encodes a single 16 bit sample to mu-law
+ ///
+ /// 16 bit PCM sample
+ /// mu-law encoded byte
+ public static byte LinearToMuLawSample(short sample)
+ {
+ int sign = (sample >> 8) & 0x80;
+ if (sign != 0)
+ sample = (short)-sample;
+ if (sample > cClip)
+ sample = cClip;
+ sample = (short)(sample + cBias);
+ int exponent = (int)MuLawCompressTable[(sample >> 7) & 0xFF];
+ int mantissa = (sample >> (exponent + 3)) & 0x0F;
+ int compressedByte = ~(sign | (exponent << 4) | mantissa);
+
+ return (byte)compressedByte;
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioCaptureClient.cs b/NAudio/CoreAudioApi/AudioCaptureClient.cs
new file mode 100644
index 00000000..6caaffbe
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioCaptureClient.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Capture Client
+ ///
+ public class AudioCaptureClient : IDisposable
+ {
+ IAudioCaptureClient audioCaptureClientInterface;
+
+ internal AudioCaptureClient(IAudioCaptureClient audioCaptureClientInterface)
+ {
+ this.audioCaptureClientInterface = audioCaptureClientInterface;
+ }
+
+ ///
+ /// Gets a pointer to the buffer
+ ///
+ /// Pointer to the buffer
+ public IntPtr GetBuffer(
+ out int numFramesToRead,
+ out AudioClientBufferFlags bufferFlags,
+ out long devicePosition,
+ out long qpcPosition)
+ {
+ IntPtr bufferPointer;
+ Marshal.ThrowExceptionForHR(audioCaptureClientInterface.GetBuffer(out bufferPointer, out numFramesToRead, out bufferFlags, out devicePosition, out qpcPosition));
+ return bufferPointer;
+ }
+
+ ///
+ /// Gets a pointer to the buffer
+ ///
+ /// Number of frames to read
+ /// Buffer flags
+ /// Pointer to the buffer
+ public IntPtr GetBuffer(
+ out int numFramesToRead,
+ out AudioClientBufferFlags bufferFlags)
+ {
+ IntPtr bufferPointer;
+ long devicePosition;
+ long qpcPosition;
+ Marshal.ThrowExceptionForHR(audioCaptureClientInterface.GetBuffer(out bufferPointer, out numFramesToRead, out bufferFlags, out devicePosition, out qpcPosition));
+ return bufferPointer;
+ }
+
+ ///
+ /// Gets the size of the next packet
+ ///
+ public int GetNextPacketSize()
+ {
+ int numFramesInNextPacket;
+ Marshal.ThrowExceptionForHR(audioCaptureClientInterface.GetNextPacketSize(out numFramesInNextPacket));
+ return numFramesInNextPacket;
+ }
+
+ ///
+ /// Release buffer
+ ///
+ /// Number of frames written
+ public void ReleaseBuffer(int numFramesWritten)
+ {
+ Marshal.ThrowExceptionForHR(audioCaptureClientInterface.ReleaseBuffer(numFramesWritten));
+ }
+
+ ///
+ /// Release the COM object
+ ///
+ public void Dispose()
+ {
+ if (audioCaptureClientInterface != null)
+ {
+ // althugh GC would do this for us, we want it done now
+ // to let us reopen WASAPI
+ Marshal.ReleaseComObject(audioCaptureClientInterface);
+ audioCaptureClientInterface = null;
+ GC.SuppressFinalize(this);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/CoreAudioApi/AudioClient.cs b/NAudio/CoreAudioApi/AudioClient.cs
new file mode 100644
index 00000000..accbdd3c
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioClient.cs
@@ -0,0 +1,303 @@
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Windows CoreAudio AudioClient
+ ///
+ public class AudioClient : IDisposable
+ {
+ private IAudioClient audioClientInterface;
+ private WaveFormat mixFormat;
+ private AudioRenderClient audioRenderClient;
+ private AudioCaptureClient audioCaptureClient;
+ private AudioClockClient audioClockClient;
+
+ internal AudioClient(IAudioClient audioClientInterface)
+ {
+ this.audioClientInterface = audioClientInterface;
+ }
+
+ ///
+ /// Retrieves the stream format that the audio engine uses for its internal processing of shared-mode streams.
+ /// Can be called before initialize
+ ///
+ public WaveFormat MixFormat
+ {
+ get
+ {
+ if (mixFormat == null)
+ {
+ IntPtr waveFormatPointer;
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetMixFormat(out waveFormatPointer));
+ var waveFormat = WaveFormat.MarshalFromPtr(waveFormatPointer);
+ Marshal.FreeCoTaskMem(waveFormatPointer);
+ mixFormat = waveFormat;
+ }
+ return mixFormat;
+ }
+ }
+
+ ///
+ /// Initializes the Audio Client
+ ///
+ /// Share Mode
+ /// Stream Flags
+ /// Buffer Duration
+ /// Periodicity
+ /// Wave Format
+ /// Audio Session GUID (can be null)
+ public void Initialize(AudioClientShareMode shareMode,
+ AudioClientStreamFlags streamFlags,
+ long bufferDuration,
+ long periodicity,
+ WaveFormat waveFormat,
+ Guid audioSessionGuid)
+ {
+ int hresult = audioClientInterface.Initialize(shareMode, streamFlags, bufferDuration, periodicity, waveFormat, ref audioSessionGuid);
+ Marshal.ThrowExceptionForHR(hresult);
+ // may have changed the mix format so reset it
+ mixFormat = null;
+ }
+
+ ///
+ /// Retrieves the size (maximum capacity) of the audio buffer associated with the endpoint. (must initialize first)
+ ///
+ public int BufferSize
+ {
+ get
+ {
+ uint bufferSize;
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetBufferSize(out bufferSize));
+ return (int) bufferSize;
+ }
+ }
+
+ ///
+ /// Retrieves the maximum latency for the current stream and can be called any time after the stream has been initialized.
+ ///
+ public long StreamLatency
+ {
+ get
+ {
+ return audioClientInterface.GetStreamLatency();
+ }
+ }
+
+ ///
+ /// Retrieves the number of frames of padding in the endpoint buffer (must initialize first)
+ ///
+ public int CurrentPadding
+ {
+ get
+ {
+ int currentPadding;
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetCurrentPadding(out currentPadding));
+ return currentPadding;
+ }
+ }
+
+ ///
+ /// Retrieves the length of the periodic interval separating successive processing passes by the audio engine on the data in the endpoint buffer.
+ /// (can be called before initialize)
+ ///
+ public long DefaultDevicePeriod
+ {
+ get
+ {
+ long defaultDevicePeriod;
+ long minimumDevicePeriod;
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetDevicePeriod(out defaultDevicePeriod, out minimumDevicePeriod));
+ return defaultDevicePeriod;
+ }
+ }
+
+ ///
+ /// Gets the minimum device period
+ /// (can be called before initialize)
+ ///
+ public long MinimumDevicePeriod
+ {
+ get
+ {
+ long defaultDevicePeriod;
+ long minimumDevicePeriod;
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetDevicePeriod(out defaultDevicePeriod, out minimumDevicePeriod));
+ return minimumDevicePeriod;
+ }
+ }
+
+ // TODO: GetService:
+ // IID_IAudioSessionControl
+ // IID_IAudioStreamVolume
+ // IID_IChannelAudioVolume
+ // IID_ISimpleAudioVolume
+
+ ///
+ /// Gets the AudioClockClient service
+ ///
+ public AudioClockClient AudioClockClient
+ {
+ get
+ {
+ if (audioClockClient == null)
+ {
+ object audioClockClientInterface;
+ var audioClockClientGuid = new Guid("CD63314F-3FBA-4a1b-812C-EF96358728E7");
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetService(audioClockClientGuid, out audioClockClientInterface));
+ audioClockClient = new AudioClockClient((IAudioClock)audioClockClientInterface);
+ }
+ return audioClockClient;
+ }
+ }
+
+ ///
+ /// Gets the AudioRenderClient service
+ ///
+ public AudioRenderClient AudioRenderClient
+ {
+ get
+ {
+ if (audioRenderClient == null)
+ {
+ object audioRenderClientInterface;
+ var audioRenderClientGuid = new Guid("F294ACFC-3146-4483-A7BF-ADDCA7C260E2");
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetService(audioRenderClientGuid, out audioRenderClientInterface));
+ audioRenderClient = new AudioRenderClient((IAudioRenderClient)audioRenderClientInterface);
+ }
+ return audioRenderClient;
+ }
+ }
+
+ ///
+ /// Gets the AudioCaptureClient service
+ ///
+ public AudioCaptureClient AudioCaptureClient
+ {
+ get
+ {
+ if (audioCaptureClient == null)
+ {
+ object audioCaptureClientInterface;
+ var audioCaptureClientGuid = new Guid("c8adbd64-e71e-48a0-a4de-185c395cd317");
+ Marshal.ThrowExceptionForHR(audioClientInterface.GetService(audioCaptureClientGuid, out audioCaptureClientInterface));
+ audioCaptureClient = new AudioCaptureClient((IAudioCaptureClient)audioCaptureClientInterface);
+ }
+ return audioCaptureClient;
+ }
+ }
+
+ ///
+ /// Determines whether if the specified output format is supported
+ ///
+ /// The share mode.
+ /// The desired format.
+ /// True if the format is supported
+ public bool IsFormatSupported(AudioClientShareMode shareMode,
+ WaveFormat desiredFormat)
+ {
+ WaveFormatExtensible closestMatchFormat;
+ return IsFormatSupported(shareMode, desiredFormat, out closestMatchFormat);
+ }
+
+ ///
+ /// Determines if the specified output format is supported in shared mode
+ ///
+ /// Share Mode
+ /// Desired Format
+ /// Output The closest match format.
+ /// True if the format is supported
+ public bool IsFormatSupported(AudioClientShareMode shareMode, WaveFormat desiredFormat, out WaveFormatExtensible closestMatchFormat)
+ {
+ int hresult = audioClientInterface.IsFormatSupported(shareMode, desiredFormat, out closestMatchFormat);
+ // S_OK is 0, S_FALSE = 1
+ if (hresult == 0)
+ {
+ // directly supported
+ return true;
+ }
+ if (hresult == 1)
+ {
+ return false;
+ }
+ if (hresult == (int)AudioClientErrors.UnsupportedFormat)
+ {
+ return false;
+ }
+ Marshal.ThrowExceptionForHR(hresult);
+ // shouldn't get here
+ throw new NotSupportedException("Unknown hresult " + hresult);
+ }
+
+ ///
+ /// Starts the audio stream
+ ///
+ public void Start()
+ {
+ audioClientInterface.Start();
+ }
+
+ ///
+ /// Stops the audio stream.
+ ///
+ public void Stop()
+ {
+ audioClientInterface.Stop();
+ }
+
+ ///
+ /// Set the Event Handle for buffer synchro.
+ ///
+ /// The Wait Handle to setup
+ public void SetEventHandle(IntPtr eventWaitHandle)
+ {
+ audioClientInterface.SetEventHandle(eventWaitHandle);
+ }
+
+ ///
+ /// Resets the audio stream
+ /// Reset is a control method that the client calls to reset a stopped audio stream.
+ /// Resetting the stream flushes all pending data and resets the audio clock stream
+ /// position to 0. This method fails if it is called on a stream that is not stopped
+ ///
+ public void Reset()
+ {
+ audioClientInterface.Reset();
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (audioClientInterface != null)
+ {
+ if (audioClockClient != null)
+ {
+ audioClockClient.Dispose();
+ audioClockClient = null;
+ }
+ if (audioRenderClient != null)
+ {
+ audioRenderClient.Dispose();
+ audioRenderClient = null;
+ }
+ if (audioCaptureClient != null)
+ {
+ audioCaptureClient.Dispose();
+ audioCaptureClient = null;
+ }
+ Marshal.ReleaseComObject(audioClientInterface);
+ audioClientInterface = null;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioClientBufferFlags.cs b/NAudio/CoreAudioApi/AudioClientBufferFlags.cs
new file mode 100644
index 00000000..0ecba084
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioClientBufferFlags.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Client Buffer Flags
+ ///
+ [Flags]
+ public enum AudioClientBufferFlags
+ {
+ ///
+ /// None
+ ///
+ None,
+ ///
+ /// AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY
+ ///
+ DataDiscontinuity = 0x1,
+ ///
+ /// AUDCLNT_BUFFERFLAGS_SILENT
+ ///
+ Silent = 0x2,
+ ///
+ /// AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR
+ ///
+ TimestampError = 0x4
+
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioClientProperties.cs b/NAudio/CoreAudioApi/AudioClientProperties.cs
new file mode 100644
index 00000000..69524e05
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioClientProperties.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// The AudioClientProperties structure is used to set the parameters that describe the properties of the client's audio stream.
+ ///
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/hh968105(v=vs.85).aspx
+ [StructLayout(LayoutKind.Sequential)]
+ public struct AudioClientProperties
+ {
+ ///
+ /// The size of the buffer for the audio stream.
+ ///
+ public UInt32 cbSize;
+ ///
+ /// Boolean value to indicate whether or not the audio stream is hardware-offloaded
+ ///
+ public int bIsOffload;
+ ///
+ /// An enumeration that is used to specify the category of the audio stream.
+ ///
+ public AudioStreamCategory eCategory;
+ ///
+ /// A bit-field describing the characteristics of the stream. Supported in Windows 8.1 and later.
+ ///
+ public AudioClientStreamOptions Options;
+ }
+}
\ No newline at end of file
diff --git a/NAudio/CoreAudioApi/AudioClientShareMode.cs b/NAudio/CoreAudioApi/AudioClientShareMode.cs
new file mode 100644
index 00000000..f9253b7f
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioClientShareMode.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// AUDCLNT_SHAREMODE
+ ///
+ public enum AudioClientShareMode
+ {
+ ///
+ /// AUDCLNT_SHAREMODE_SHARED,
+ ///
+ Shared,
+ ///
+ /// AUDCLNT_SHAREMODE_EXCLUSIVE
+ ///
+ Exclusive,
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioClientStreamFlags.cs b/NAudio/CoreAudioApi/AudioClientStreamFlags.cs
new file mode 100644
index 00000000..d9414dce
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioClientStreamFlags.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// AUDCLNT_STREAMFLAGS
+ ///
+ [Flags]
+ public enum AudioClientStreamFlags
+ {
+ ///
+ /// None
+ ///
+ None,
+ ///
+ /// AUDCLNT_STREAMFLAGS_CROSSPROCESS
+ ///
+ CrossProcess = 0x00010000,
+ ///
+ /// AUDCLNT_STREAMFLAGS_LOOPBACK
+ ///
+ Loopback = 0x00020000,
+ ///
+ /// AUDCLNT_STREAMFLAGS_EVENTCALLBACK
+ ///
+ EventCallback = 0x00040000,
+ ///
+ /// AUDCLNT_STREAMFLAGS_NOPERSIST
+ ///
+ NoPersist = 0x00080000,
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioClientStreamOptions.cs b/NAudio/CoreAudioApi/AudioClientStreamOptions.cs
new file mode 100644
index 00000000..918bb9a9
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioClientStreamOptions.cs
@@ -0,0 +1,17 @@
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Defines values that describe the characteristics of an audio stream.
+ ///
+ public enum AudioClientStreamOptions
+ {
+ ///
+ /// No stream options.
+ ///
+ None = 0,
+ ///
+ /// The audio stream is a 'raw' stream that bypasses all signal processing except for endpoint specific, always-on processing in the APO, driver, and hardware.
+ ///
+ Raw = 0x1
+ }
+}
\ No newline at end of file
diff --git a/NAudio/CoreAudioApi/AudioClockClient.cs b/NAudio/CoreAudioApi/AudioClockClient.cs
new file mode 100644
index 00000000..6701a87d
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioClockClient.cs
@@ -0,0 +1,128 @@
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Clock Client
+ ///
+ public class AudioClockClient : IDisposable
+ {
+ IAudioClock audioClockClientInterface;
+
+ internal AudioClockClient(IAudioClock audioClockClientInterface)
+ {
+ this.audioClockClientInterface = audioClockClientInterface;
+
+ //Stopwatch.GetTimestamp();
+ //Stopwatch.Frequency
+ }
+
+ ///
+ /// Characteristics
+ ///
+ public int Characteristics
+ {
+ get
+ {
+ uint characteristics;
+ Marshal.ThrowExceptionForHR(audioClockClientInterface.GetCharacteristics(out characteristics));
+ return (int)characteristics;
+ }
+ }
+
+ ///
+ /// Frequency
+ ///
+ public ulong Frequency
+ {
+ get
+ {
+ ulong freq;
+ Marshal.ThrowExceptionForHR(audioClockClientInterface.GetFrequency(out freq));
+ return freq;
+ }
+ }
+
+ ///
+ /// Get Position
+ ///
+ public bool GetPosition(out ulong position, out ulong qpcPosition)
+ {
+ var hr = audioClockClientInterface.GetPosition(out position, out qpcPosition);
+ if (hr == -1) return false;
+ Marshal.ThrowExceptionForHR(hr);
+ return true;
+ }
+
+ ///
+ /// Adjusted Position
+ ///
+ public ulong AdjustedPosition
+ {
+ get
+ {
+ // figure out ticks per byte (for later)
+ var byteLatency = (TimeSpan.TicksPerSecond / Frequency);
+
+ ulong pos, qpos;
+ int cnt = 0;
+ while (!GetPosition(out pos, out qpos))
+ {
+ if (++cnt == 5)
+ {
+ // we've tried too many times, so now we have to just run with what we have...
+ break;
+ }
+ }
+
+ if (Stopwatch.IsHighResolution)
+ {
+ // cool, we can adjust our position appropriately
+
+ // get the current qpc count (in ticks)
+ var qposNow = (ulong)((Stopwatch.GetTimestamp() * 10000000M) / Stopwatch.Frequency);
+
+ // find out how many ticks has passed since the device reported the position
+ var qposDiff = (qposNow - qpos) / 100;
+
+ // find out how many byte would have played in that time span
+ var bytes = qposDiff / byteLatency;
+
+ // add it to the position
+ pos += bytes;
+ }
+ return pos;
+ }
+ }
+
+ ///
+ /// Can Adjust Position
+ ///
+ public bool CanAdjustPosition
+ {
+ get { return Stopwatch.IsHighResolution; }
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (audioClockClientInterface != null)
+ {
+ // althugh GC would do this for us, we want it done now
+ // to let us reopen WASAPI
+ Marshal.ReleaseComObject(audioClockClientInterface);
+ audioClockClientInterface = null;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioEndpointVolume.cs b/NAudio/CoreAudioApi/AudioEndpointVolume.cs
new file mode 100644
index 00000000..748e98a1
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioEndpointVolume.cs
@@ -0,0 +1,210 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Endpoint Volume
+ ///
+ public class AudioEndpointVolume : IDisposable
+ {
+ private readonly IAudioEndpointVolume audioEndPointVolume;
+ private readonly AudioEndpointVolumeChannels channels;
+ private readonly AudioEndpointVolumeStepInformation stepInformation;
+ private readonly AudioEndpointVolumeVolumeRange volumeRange;
+ private readonly EEndpointHardwareSupport hardwareSupport;
+ private AudioEndpointVolumeCallback callBack;
+
+ ///
+ /// On Volume Notification
+ ///
+ public event AudioEndpointVolumeNotificationDelegate OnVolumeNotification;
+
+ ///
+ /// Volume Range
+ ///
+ public AudioEndpointVolumeVolumeRange VolumeRange
+ {
+ get
+ {
+ return volumeRange;
+ }
+ }
+
+ ///
+ /// Hardware Support
+ ///
+ public EEndpointHardwareSupport HardwareSupport
+ {
+ get
+ {
+ return hardwareSupport;
+ }
+ }
+
+ ///
+ /// Step Information
+ ///
+ public AudioEndpointVolumeStepInformation StepInformation
+ {
+ get
+ {
+ return stepInformation;
+ }
+ }
+
+ ///
+ /// Channels
+ ///
+ public AudioEndpointVolumeChannels Channels
+ {
+ get
+ {
+ return channels;
+ }
+ }
+
+ ///
+ /// Master Volume Level
+ ///
+ public float MasterVolumeLevel
+ {
+ get
+ {
+ float result;
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.GetMasterVolumeLevel(out result));
+ return result;
+ }
+ set
+ {
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.SetMasterVolumeLevel(value, Guid.Empty));
+ }
+ }
+
+ ///
+ /// Master Volume Level Scalar
+ ///
+ public float MasterVolumeLevelScalar
+ {
+ get
+ {
+ float result;
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.GetMasterVolumeLevelScalar(out result));
+ return result;
+ }
+ set
+ {
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.SetMasterVolumeLevelScalar(value, Guid.Empty));
+ }
+ }
+
+ ///
+ /// Mute
+ ///
+ public bool Mute
+ {
+ get
+ {
+ bool result;
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.GetMute(out result));
+ return result;
+ }
+ set
+ {
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.SetMute(value, Guid.Empty));
+ }
+ }
+
+ ///
+ /// Volume Step Up
+ ///
+ public void VolumeStepUp()
+ {
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.VolumeStepUp(Guid.Empty));
+ }
+
+ ///
+ /// Volume Step Down
+ ///
+ public void VolumeStepDown()
+ {
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.VolumeStepDown(Guid.Empty));
+ }
+
+ ///
+ /// Creates a new Audio endpoint volume
+ ///
+ /// IAudioEndpointVolume COM interface
+ internal AudioEndpointVolume(IAudioEndpointVolume realEndpointVolume)
+ {
+ uint hardwareSupp;
+
+ audioEndPointVolume = realEndpointVolume;
+ channels = new AudioEndpointVolumeChannels(audioEndPointVolume);
+ stepInformation = new AudioEndpointVolumeStepInformation(audioEndPointVolume);
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.QueryHardwareSupport(out hardwareSupp));
+ hardwareSupport = (EEndpointHardwareSupport)hardwareSupp;
+ volumeRange = new AudioEndpointVolumeVolumeRange(audioEndPointVolume);
+ callBack = new AudioEndpointVolumeCallback(this);
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.RegisterControlChangeNotify(callBack));
+ }
+
+ internal void FireNotification(AudioVolumeNotificationData notificationData)
+ {
+ AudioEndpointVolumeNotificationDelegate del = OnVolumeNotification;
+ if (del != null)
+ {
+ del(notificationData);
+ }
+ }
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (callBack != null)
+ {
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.UnregisterControlChangeNotify(callBack));
+ callBack = null;
+ }
+ GC.SuppressFinalize(this);
+
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ~AudioEndpointVolume()
+ {
+ Dispose();
+ }
+
+ #endregion
+
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioEndpointVolumeCallback.cs b/NAudio/CoreAudioApi/AudioEndpointVolumeCallback.cs
new file mode 100644
index 00000000..8f5cc9ae
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioEndpointVolumeCallback.cs
@@ -0,0 +1,73 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ (Modified for NAudio by Mark Heath)
+
+ */
+
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ // This class implements the IAudioEndpointVolumeCallback interface,
+ // it is implemented in this class because implementing it on AudioEndpointVolume
+ // (where the functionality is really wanted, would cause the OnNotify function
+ // to show up in the public API.
+ internal class AudioEndpointVolumeCallback : IAudioEndpointVolumeCallback
+ {
+ private readonly AudioEndpointVolume parent;
+
+ internal AudioEndpointVolumeCallback(AudioEndpointVolume parent)
+ {
+ this.parent = parent;
+ }
+
+ public void OnNotify(IntPtr notifyData)
+ {
+ //Since AUDIO_VOLUME_NOTIFICATION_DATA is dynamic in length based on the
+ //number of audio channels available we cannot just call PtrToStructure
+ //to get all data, thats why it is split up into two steps, first the static
+ //data is marshalled into the data structure, then with some IntPtr math the
+ //remaining floats are read from memory.
+ //
+ var data = (AudioVolumeNotificationDataStruct)Marshal.PtrToStructure(notifyData, typeof(AudioVolumeNotificationDataStruct));
+
+ //Determine offset in structure of the first float
+ var offset = Marshal.OffsetOf(typeof(AudioVolumeNotificationDataStruct), "ChannelVolume");
+ //Determine offset in memory of the first float
+ var firstFloatPtr = (IntPtr)((long)notifyData + (long)offset);
+
+ var voldata = new float[data.nChannels];
+
+ //Read all floats from memory.
+ for (int i = 0; i < data.nChannels; i++)
+ {
+ voldata[i] = (float)Marshal.PtrToStructure(firstFloatPtr, typeof(float));
+ }
+
+ //Create combined structure and Fire Event in parent class.
+ var notificationData = new AudioVolumeNotificationData(data.guidEventContext, data.bMuted, data.fMasterVolume, voldata);
+ parent.FireNotification(notificationData);
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioEndpointVolumeChannel.cs b/NAudio/CoreAudioApi/AudioEndpointVolumeChannel.cs
new file mode 100644
index 00000000..5d738c0f
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioEndpointVolumeChannel.cs
@@ -0,0 +1,78 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Endpoint Volume Channel
+ ///
+ public class AudioEndpointVolumeChannel
+ {
+ private readonly uint channel;
+ private readonly IAudioEndpointVolume audioEndpointVolume;
+
+ internal AudioEndpointVolumeChannel(IAudioEndpointVolume parent, int channel)
+ {
+ this.channel = (uint)channel;
+ audioEndpointVolume = parent;
+ }
+
+ ///
+ /// Volume Level
+ ///
+ public float VolumeLevel
+ {
+ get
+ {
+ float result;
+ Marshal.ThrowExceptionForHR(audioEndpointVolume.GetChannelVolumeLevel(channel,out result));
+ return result;
+ }
+ set
+ {
+ Marshal.ThrowExceptionForHR(audioEndpointVolume.SetChannelVolumeLevel(channel, value,Guid.Empty));
+ }
+ }
+
+ ///
+ /// Volume Level Scalar
+ ///
+ public float VolumeLevelScalar
+ {
+ get
+ {
+ float result;
+ Marshal.ThrowExceptionForHR(audioEndpointVolume.GetChannelVolumeLevelScalar(channel, out result));
+ return result;
+ }
+ set
+ {
+ Marshal.ThrowExceptionForHR(audioEndpointVolume.SetChannelVolumeLevelScalar(channel, value, Guid.Empty));
+ }
+ }
+
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioEndpointVolumeChannels.cs b/NAudio/CoreAudioApi/AudioEndpointVolumeChannels.cs
new file mode 100644
index 00000000..fbaff1a0
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioEndpointVolumeChannels.cs
@@ -0,0 +1,76 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Endpoint Volume Channels
+ ///
+ public class AudioEndpointVolumeChannels
+ {
+ readonly IAudioEndpointVolume audioEndPointVolume;
+ readonly AudioEndpointVolumeChannel[] channels;
+
+ ///
+ /// Channel Count
+ ///
+ public int Count
+ {
+ get
+ {
+ int result;
+ Marshal.ThrowExceptionForHR(audioEndPointVolume.GetChannelCount(out result));
+ return result;
+ }
+ }
+
+ ///
+ /// Indexer - get a specific channel
+ ///
+ public AudioEndpointVolumeChannel this[int index]
+ {
+ get
+ {
+ return channels[index];
+ }
+ }
+
+ internal AudioEndpointVolumeChannels(IAudioEndpointVolume parent)
+ {
+ int ChannelCount;
+ audioEndPointVolume = parent;
+
+ ChannelCount = Count;
+ channels = new AudioEndpointVolumeChannel[ChannelCount];
+ for (int i = 0; i < ChannelCount; i++)
+ {
+ channels[i] = new AudioEndpointVolumeChannel(audioEndPointVolume, i);
+ }
+ }
+
+
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioEndpointVolumeNotificationDelegate.cs b/NAudio/CoreAudioApi/AudioEndpointVolumeNotificationDelegate.cs
new file mode 100644
index 00000000..d72fc27d
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioEndpointVolumeNotificationDelegate.cs
@@ -0,0 +1,32 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Endpoint Volume Notifiaction Delegate
+ ///
+ /// Audio Volume Notification Data
+ public delegate void AudioEndpointVolumeNotificationDelegate(AudioVolumeNotificationData data);
+}
diff --git a/NAudio/CoreAudioApi/AudioEndpointVolumeStepInformation.cs b/NAudio/CoreAudioApi/AudioEndpointVolumeStepInformation.cs
new file mode 100644
index 00000000..0269aaf9
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioEndpointVolumeStepInformation.cs
@@ -0,0 +1,57 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Endpoint Volume Step Information
+ ///
+ public class AudioEndpointVolumeStepInformation
+ {
+ private readonly uint step;
+ private readonly uint stepCount;
+
+ internal AudioEndpointVolumeStepInformation(IAudioEndpointVolume parent)
+ {
+ Marshal.ThrowExceptionForHR(parent.GetVolumeStepInfo(out step, out stepCount));
+ }
+
+ ///
+ /// Step
+ ///
+ public uint Step
+ {
+ get { return step; }
+ }
+
+ ///
+ /// StepCount
+ ///
+ public uint StepCount
+ {
+ get { return stepCount; }
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioEndpointVolumeVolumeRange.cs b/NAudio/CoreAudioApi/AudioEndpointVolumeVolumeRange.cs
new file mode 100644
index 00000000..4454a103
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioEndpointVolumeVolumeRange.cs
@@ -0,0 +1,67 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// modified for NAudio
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Endpoint Volume Volume Range
+ ///
+ public class AudioEndpointVolumeVolumeRange
+ {
+ readonly float volumeMinDecibels;
+ readonly float volumeMaxDecibels;
+ readonly float volumeIncrementDecibels;
+
+ internal AudioEndpointVolumeVolumeRange(IAudioEndpointVolume parent)
+ {
+ Marshal.ThrowExceptionForHR(parent.GetVolumeRange(out volumeMinDecibels,out volumeMaxDecibels,out volumeIncrementDecibels));
+ }
+
+ ///
+ /// Minimum Decibels
+ ///
+ public float MinDecibels
+ {
+ get { return volumeMinDecibels; }
+ }
+
+ ///
+ /// Maximum Decibels
+ ///
+ public float MaxDecibels
+ {
+ get { return volumeMaxDecibels; }
+ }
+
+ ///
+ /// Increment Decibels
+ ///
+ public float IncrementDecibels
+ {
+ get { return volumeIncrementDecibels; }
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioMeterInformation.cs b/NAudio/CoreAudioApi/AudioMeterInformation.cs
new file mode 100644
index 00000000..3f724904
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioMeterInformation.cs
@@ -0,0 +1,83 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Meter Information
+ ///
+ public class AudioMeterInformation
+ {
+ private readonly IAudioMeterInformation audioMeterInformation;
+ private readonly EEndpointHardwareSupport hardwareSupport;
+ private readonly AudioMeterInformationChannels channels;
+
+ internal AudioMeterInformation(IAudioMeterInformation realInterface)
+ {
+ int hardwareSupp;
+
+ audioMeterInformation = realInterface;
+ Marshal.ThrowExceptionForHR(audioMeterInformation.QueryHardwareSupport(out hardwareSupp));
+ hardwareSupport = (EEndpointHardwareSupport)hardwareSupp;
+ channels = new AudioMeterInformationChannels(audioMeterInformation);
+
+ }
+
+ ///
+ /// Peak Values
+ ///
+ public AudioMeterInformationChannels PeakValues
+ {
+ get
+ {
+ return channels;
+ }
+ }
+
+ ///
+ /// Hardware Support
+ ///
+ public EEndpointHardwareSupport HardwareSupport
+ {
+ get
+ {
+ return hardwareSupport;
+ }
+ }
+
+ ///
+ /// Master Peak Value
+ ///
+ public float MasterPeakValue
+ {
+ get
+ {
+ float result;
+ Marshal.ThrowExceptionForHR(audioMeterInformation.GetPeakValue(out result));
+ return result;
+ }
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioMeterInformationChannels.cs b/NAudio/CoreAudioApi/AudioMeterInformationChannels.cs
new file mode 100644
index 00000000..9dba54f2
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioMeterInformationChannels.cs
@@ -0,0 +1,70 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Meter Information Channels
+ ///
+ public class AudioMeterInformationChannels
+ {
+ readonly IAudioMeterInformation audioMeterInformation;
+
+ ///
+ /// Metering Channel Count
+ ///
+ public int Count
+ {
+ get
+ {
+ int result;
+ Marshal.ThrowExceptionForHR(audioMeterInformation.GetMeteringChannelCount(out result));
+ return result;
+ }
+ }
+
+ ///
+ /// Get Peak value
+ ///
+ /// Channel index
+ /// Peak value
+ public float this[int index]
+ {
+ get
+ {
+ var peakValues = new float[Count];
+ GCHandle Params = GCHandle.Alloc(peakValues, GCHandleType.Pinned);
+ Marshal.ThrowExceptionForHR(audioMeterInformation.GetChannelsPeakValues(peakValues.Length, Params.AddrOfPinnedObject()));
+ Params.Free();
+ return peakValues[index];
+ }
+ }
+
+ internal AudioMeterInformationChannels(IAudioMeterInformation parent)
+ {
+ audioMeterInformation = parent;
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioRenderClient.cs b/NAudio/CoreAudioApi/AudioRenderClient.cs
new file mode 100644
index 00000000..f380715d
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioRenderClient.cs
@@ -0,0 +1,56 @@
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Render Client
+ ///
+ public class AudioRenderClient : IDisposable
+ {
+ IAudioRenderClient audioRenderClientInterface;
+
+ internal AudioRenderClient(IAudioRenderClient audioRenderClientInterface)
+ {
+ this.audioRenderClientInterface = audioRenderClientInterface;
+ }
+
+ ///
+ /// Gets a pointer to the buffer
+ ///
+ /// Number of frames requested
+ /// Pointer to the buffer
+ public IntPtr GetBuffer(int numFramesRequested)
+ {
+ IntPtr bufferPointer;
+ Marshal.ThrowExceptionForHR(audioRenderClientInterface.GetBuffer(numFramesRequested, out bufferPointer));
+ return bufferPointer;
+ }
+
+ ///
+ /// Release buffer
+ ///
+ /// Number of frames written
+ /// Buffer flags
+ public void ReleaseBuffer(int numFramesWritten,AudioClientBufferFlags bufferFlags)
+ {
+ Marshal.ThrowExceptionForHR(audioRenderClientInterface.ReleaseBuffer(numFramesWritten, bufferFlags));
+ }
+
+ ///
+ /// Release the COM object
+ ///
+ public void Dispose()
+ {
+ if (audioRenderClientInterface != null)
+ {
+ // althugh GC would do this for us, we want it done now
+ // to let us reopen WASAPI
+ Marshal.ReleaseComObject(audioRenderClientInterface);
+ audioRenderClientInterface = null;
+ GC.SuppressFinalize(this);
+ }
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioSessionControl.cs b/NAudio/CoreAudioApi/AudioSessionControl.cs
new file mode 100644
index 00000000..27b0cc2c
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioSessionControl.cs
@@ -0,0 +1,156 @@
+// -----------------------------------------
+// milligan22963 - implemented to work with nAudio
+// 12/2014
+// -----------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// AudioSessionControl object for information
+ /// regarding an audio session
+ ///
+ public class AudioSessionControl : IDisposable
+ {
+ private IAudioSessionControl audioSessionControlInterface;
+ private AudioSessionEventsCallback audioSessionEventCallback = null;
+
+ internal AudioSessionControl(IAudioSessionControl audioSessionControl)
+ {
+ audioSessionControlInterface = audioSessionControl;
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ~AudioSessionControl()
+ {
+ if (audioSessionEventCallback != null)
+ {
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.UnregisterAudioSessionNotification(audioSessionEventCallback));
+ }
+ Dispose();
+ }
+
+ #endregion
+
+ ///
+ /// The current state of the audio session
+ ///
+ public AudioSessionState State
+ {
+ get
+ {
+ AudioSessionState state;
+
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.GetState(out state));
+
+ return state;
+ }
+ }
+
+ ///
+ /// The name of the audio session
+ ///
+ public string DisplayName
+ {
+ get
+ {
+ string displayName = String.Empty;
+
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.GetDisplayName(out displayName));
+
+ return displayName;
+ }
+ set
+ {
+ if (value != String.Empty)
+ {
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.SetDisplayName(value, Guid.Empty));
+ }
+ }
+ }
+
+ ///
+ /// the path to the icon shown in the mixer
+ ///
+ public string IconPath
+ {
+ get
+ {
+ string iconPath = String.Empty;
+
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.GetIconPath(out iconPath));
+
+ return iconPath;
+ }
+ set
+ {
+ if (value != String.Empty)
+ {
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.SetIconPath(value, Guid.Empty));
+ }
+ }
+ }
+
+ ///
+ /// the grouping param for an audio session grouping
+ ///
+ ///
+ public Guid GetGroupingParam()
+ {
+ Guid groupingId = Guid.Empty;
+
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.GetGroupingParam(out groupingId));
+
+ return groupingId;
+ }
+
+ ///
+ /// For chanigng the grouping param and supplying the context of said change
+ ///
+ ///
+ ///
+ public void SetGroupingParam(Guid groupingId, Guid context)
+ {
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.SetGroupingParam(groupingId, context));
+ }
+
+ ///
+ /// Registers an even client for callbacks
+ ///
+ ///
+ public void RegisterEventClient(IAudioSessionEventsHandler eventClient)
+ {
+ // we could have an array or list of listeners if we like
+ audioSessionEventCallback = new AudioSessionEventsCallback(eventClient);
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.RegisterAudioSessionNotification(audioSessionEventCallback));
+ }
+
+ ///
+ /// Unregisters an event client from receiving callbacks
+ ///
+ ///
+ public void UnRegisterEventClient(IAudioSessionEventsHandler eventClient)
+ {
+ // if one is registered, let it go
+ if (audioSessionEventCallback != null)
+ {
+ Marshal.ThrowExceptionForHR(audioSessionControlInterface.UnregisterAudioSessionNotification(audioSessionEventCallback));
+ }
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioSessionEventsCallback.cs b/NAudio/CoreAudioApi/AudioSessionEventsCallback.cs
new file mode 100644
index 00000000..f3655109
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioSessionEventsCallback.cs
@@ -0,0 +1,119 @@
+// -----------------------------------------
+// milligan22963 - implemented to work with nAudio
+// 12/2014
+// -----------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// AudioSessionEvents callback implementation
+ ///
+ public class AudioSessionEventsCallback : IAudioSessionEvents
+ {
+ private readonly IAudioSessionEventsHandler audioSessionEventsHandler;
+
+ internal AudioSessionEventsCallback(IAudioSessionEventsHandler handler)
+ {
+ audioSessionEventsHandler = handler;
+ }
+
+ ///
+ /// Notifies the client that the display name for the session has changed.
+ ///
+ /// The new display name for the session.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ public int OnDisplayNameChanged(
+ [In] [MarshalAs(UnmanagedType.LPWStr)] string displayName,
+ [In] ref Guid eventContext)
+ {
+ return 0;
+ }
+
+ ///
+ /// Notifies the client that the display icon for the session has changed.
+ ///
+ /// The path for the new display icon for the session.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ public int OnIconPathChanged(
+ [In] [MarshalAs(UnmanagedType.LPWStr)] string iconPath,
+ [In] ref Guid eventContext)
+ {
+ return 0;
+ }
+
+ ///
+ /// Notifies the client that the volume level or muting state of the session has changed.
+ ///
+ /// The new volume level for the audio session.
+ /// The new muting state.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ public int OnSimpleVolumeChanged(
+ [In] [MarshalAs(UnmanagedType.R4)] float volume,
+ [In] [MarshalAs(UnmanagedType.Bool)] bool isMuted,
+ [In] ref Guid eventContext)
+ {
+ audioSessionEventsHandler.OnVolumeChanged(volume, isMuted);
+
+ return 0;
+ }
+
+ ///
+ /// Notifies the client that the volume level of an audio channel in the session submix has changed.
+ ///
+ /// The channel count.
+ /// An array of volumnes cooresponding with each channel index.
+ /// The number of the channel whose volume level changed.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ public int OnChannelVolumeChanged(
+ [In] [MarshalAs(UnmanagedType.U4)] UInt32 channelCount,
+ [In] [MarshalAs(UnmanagedType.SysInt)] IntPtr newVolumes, // Pointer to float array
+ [In] [MarshalAs(UnmanagedType.U4)] UInt32 channelIndex,
+ [In] ref Guid eventContext)
+ {
+ return 0;
+ }
+
+ ///
+ /// Notifies the client that the grouping parameter for the session has changed.
+ ///
+ /// The new grouping parameter for the session.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ public int OnGroupingParamChanged(
+ [In] ref Guid groupingId,
+ [In] ref Guid eventContext)
+ {
+ return 0;
+ }
+
+ ///
+ /// Notifies the client that the stream-activity state of the session has changed.
+ ///
+ /// The new session state.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ public int OnStateChanged(
+ [In] AudioSessionState state)
+ {
+ return 0;
+ }
+
+ ///
+ /// Notifies the client that the session has been disconnected.
+ ///
+ /// The reason that the audio session was disconnected.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ public int OnSessionDisconnected(
+ [In] AudioSessionDisconnectReason disconnectReason)
+ {
+ return 0;
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioSessionManager.cs b/NAudio/CoreAudioApi/AudioSessionManager.cs
new file mode 100644
index 00000000..f7f6c0ff
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioSessionManager.cs
@@ -0,0 +1,70 @@
+// -----------------------------------------
+// milligan22963 - implemented to work with nAudio
+// 12/2014
+// -----------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// AudioSessionManager
+ ///
+ /// Designed to manage audio sessions and in particuar the
+ /// SimpleAudioVolume interface to adjust a session volume
+ ///
+ public class AudioSessionManager
+ {
+ private IAudioSessionManager audioSessionInterface;
+
+ private SimpleAudioVolume simpleAudioVolume = null;
+ private AudioSessionControl audioSessionControl = null;
+
+ internal AudioSessionManager(IAudioSessionManager audioSessionManager)
+ {
+ audioSessionInterface = audioSessionManager;
+ }
+
+ ///
+ /// SimpleAudioVolume object
+ /// for adjusting the volume for the user session
+ ///
+ public SimpleAudioVolume SimpleAudioVolume
+ {
+ get
+ {
+ if (simpleAudioVolume == null)
+ {
+ ISimpleAudioVolume simpleAudioInterface;
+
+ audioSessionInterface.GetSimpleAudioVolume(Guid.Empty, 0, out simpleAudioInterface);
+
+ simpleAudioVolume = new SimpleAudioVolume(simpleAudioInterface);
+ }
+ return simpleAudioVolume;
+ }
+ }
+
+ ///
+ /// AudioSessionControl object
+ /// for registring for callbacks and other session information
+ ///
+ public AudioSessionControl AudioSessionControl
+ {
+ get
+ {
+ if (audioSessionControl == null)
+ {
+ IAudioSessionControl audioSessionControlInterface;
+
+ audioSessionInterface.GetAudioSessionControl(Guid.Empty, 0, out audioSessionControlInterface);
+
+ audioSessionControl = new AudioSessionControl(audioSessionControlInterface);
+ }
+ return audioSessionControl;
+ }
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/AudioStreamCategory.cs b/NAudio/CoreAudioApi/AudioStreamCategory.cs
new file mode 100644
index 00000000..fab166d9
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioStreamCategory.cs
@@ -0,0 +1,41 @@
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Specifies the category of an audio stream.
+ ///
+ public enum AudioStreamCategory
+ {
+ ///
+ /// Other audio stream.
+ ///
+ Other = 0,
+ ///
+ /// Media that will only stream when the app is in the foreground.
+ ///
+ ForegroundOnlyMedia,
+ ///
+ /// Media that can be streamed when the app is in the background.
+ ///
+ BackgroundCapableMedia,
+ ///
+ /// Real-time communications, such as VOIP or chat.
+ ///
+ Communications,
+ ///
+ /// Alert sounds.
+ ///
+ Alerts,
+ ///
+ /// Sound effects.
+ ///
+ SoundEffects,
+ ///
+ /// Game sound effects.
+ ///
+ GameEffects,
+ ///
+ /// Background audio for games.
+ ///
+ GameMedia,
+ }
+}
\ No newline at end of file
diff --git a/NAudio/CoreAudioApi/AudioVolumeNotificationData.cs b/NAudio/CoreAudioApi/AudioVolumeNotificationData.cs
new file mode 100644
index 00000000..bbfc363a
--- /dev/null
+++ b/NAudio/CoreAudioApi/AudioVolumeNotificationData.cs
@@ -0,0 +1,93 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Volume Notification Data
+ ///
+ public class AudioVolumeNotificationData
+ {
+ private readonly Guid eventContext;
+ private readonly bool muted;
+ private readonly float masterVolume;
+ private readonly int channels;
+ private readonly float[] channelVolume;
+
+ ///
+ /// Event Context
+ ///
+ public Guid EventContext
+ {
+ get { return eventContext; }
+ }
+
+ ///
+ /// Muted
+ ///
+ public bool Muted
+ {
+ get { return muted; }
+ }
+
+ ///
+ /// Master Volume
+ ///
+ public float MasterVolume
+ {
+ get { return masterVolume; }
+ }
+
+ ///
+ /// Channels
+ ///
+ public int Channels
+ {
+ get { return channels; }
+ }
+
+ ///
+ /// Channel Volume
+ ///
+ public float[] ChannelVolume
+ {
+ get { return channelVolume; }
+ }
+
+ ///
+ /// Audio Volume Notification Data
+ ///
+ ///
+ ///
+ ///
+ ///
+ public AudioVolumeNotificationData(Guid eventContext, bool muted, float masterVolume, float[] channelVolume)
+ {
+ this.eventContext = eventContext;
+ this.muted = muted;
+ this.masterVolume = masterVolume;
+ channels = channelVolume.Length;
+ this.channelVolume = channelVolume;
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/DataFlow.cs b/NAudio/CoreAudioApi/DataFlow.cs
new file mode 100644
index 00000000..b7dbd334
--- /dev/null
+++ b/NAudio/CoreAudioApi/DataFlow.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// The EDataFlow enumeration defines constants that indicate the direction
+ /// in which audio data flows between an audio endpoint device and an application
+ ///
+ public enum DataFlow
+ {
+ ///
+ /// Audio rendering stream.
+ /// Audio data flows from the application to the audio endpoint device, which renders the stream.
+ ///
+ Render,
+ ///
+ /// Audio capture stream. Audio data flows from the audio endpoint device that captures the stream,
+ /// to the application
+ ///
+ Capture,
+ ///
+ /// Audio rendering or capture stream. Audio data can flow either from the application to the audio
+ /// endpoint device, or from the audio endpoint device to the application.
+ ///
+ All
+ };
+
+
+}
diff --git a/NAudio/CoreAudioApi/DeviceState.cs b/NAudio/CoreAudioApi/DeviceState.cs
new file mode 100644
index 00000000..6a815247
--- /dev/null
+++ b/NAudio/CoreAudioApi/DeviceState.cs
@@ -0,0 +1,55 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// adapted for NAudio,
+// updated to be in line with http://msdn.microsoft.com/en-us/library/dd370823%28v=vs.85%29.aspx
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Device State
+ ///
+ [Flags]
+ public enum DeviceState
+ {
+ ///
+ /// DEVICE_STATE_ACTIVE
+ ///
+ Active = 0x00000001,
+ ///
+ /// DEVICE_STATE_DISABLED
+ ///
+ Disabled = 0x00000002,
+ ///
+ /// DEVICE_STATE_NOTPRESENT
+ ///
+ NotPresent = 0x00000004,
+ ///
+ /// DEVICE_STATE_UNPLUGGED
+ ///
+ Unplugged = 0x00000008,
+ ///
+ /// DEVICE_STATEMASK_ALL
+ ///
+ All = 0x0000000F
+ }
+}
diff --git a/NAudio/CoreAudioApi/EEndpointHardwareSupport.cs b/NAudio/CoreAudioApi/EEndpointHardwareSupport.cs
new file mode 100644
index 00000000..084c51aa
--- /dev/null
+++ b/NAudio/CoreAudioApi/EEndpointHardwareSupport.cs
@@ -0,0 +1,45 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Endpoint Hardware Support
+ ///
+ [Flags]
+ public enum EEndpointHardwareSupport
+ {
+ ///
+ /// Volume
+ ///
+ Volume = 0x00000001,
+ ///
+ /// Mute
+ ///
+ Mute = 0x00000002,
+ ///
+ /// Meter
+ ///
+ Meter = 0x00000004
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/AudioVolumeNotificationDataStruct.cs b/NAudio/CoreAudioApi/Interfaces/AudioVolumeNotificationDataStruct.cs
new file mode 100644
index 00000000..19d48391
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/AudioVolumeNotificationDataStruct.cs
@@ -0,0 +1,52 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+using System;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ internal struct AudioVolumeNotificationDataStruct
+ {
+ public Guid guidEventContext;
+ public bool bMuted;
+ public float fMasterVolume;
+ public uint nChannels;
+ public float ChannelVolume;
+
+ //Code Should Compile at warning level4 without any warnings,
+ //However this struct will give us Warning CS0649: Field [Fieldname]
+ //is never assigned to, and will always have its default value
+ //You can disable CS0649 in the project options but that will disable
+ //the warning for the whole project, it's a nice warning and we do want
+ //it in other places so we make a nice dummy function to keep the compiler
+ //happy.
+ private void FixCS0649()
+ {
+ guidEventContext = Guid.Empty;
+ bMuted = false;
+ fMasterVolume = 0;
+ nChannels = 0;
+ ChannelVolume = 0;
+ }
+
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/Blob.cs b/NAudio/CoreAudioApi/Interfaces/Blob.cs
new file mode 100644
index 00000000..3937228a
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/Blob.cs
@@ -0,0 +1,49 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// Adapted for NAudio
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ internal struct Blob
+ {
+ public int Length;
+ public IntPtr Data;
+
+ //Code Should Compile at warning level4 without any warnings,
+ //However this struct will give us Warning CS0649: Field [Fieldname]
+ //is never assigned to, and will always have its default value
+ //You can disable CS0649 in the project options but that will disable
+ //the warning for the whole project, it's a nice warning and we do want
+ //it in other places so we make a nice dummy function to keep the compiler
+ //happy.
+ private void FixCS0649()
+ {
+ Length = 0;
+ Data = IntPtr.Zero;
+ }
+ }
+}
+
diff --git a/NAudio/CoreAudioApi/Interfaces/ClsCtx.cs b/NAudio/CoreAudioApi/Interfaces/ClsCtx.cs
new file mode 100644
index 00000000..13ab7ae6
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/ClsCtx.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// is defined in WTypes.h
+ ///
+ [Flags]
+ enum ClsCtx
+ {
+ INPROC_SERVER = 0x1,
+ INPROC_HANDLER = 0x2,
+ LOCAL_SERVER = 0x4,
+ INPROC_SERVER16 = 0x8,
+ REMOTE_SERVER = 0x10,
+ INPROC_HANDLER16 = 0x20,
+ //RESERVED1 = 0x40,
+ //RESERVED2 = 0x80,
+ //RESERVED3 = 0x100,
+ //RESERVED4 = 0x200,
+ NO_CODE_DOWNLOAD = 0x400,
+ //RESERVED5 = 0x800,
+ NO_CUSTOM_MARSHAL = 0x1000,
+ ENABLE_CODE_DOWNLOAD = 0x2000,
+ NO_FAILURE_LOG = 0x4000,
+ DISABLE_AAA = 0x8000,
+ ENABLE_AAA = 0x10000,
+ FROM_DEFAULT_CONTEXT = 0x20000,
+ ACTIVATE_32_BIT_SERVER = 0x40000,
+ ACTIVATE_64_BIT_SERVER = 0x80000,
+ ENABLE_CLOAKING = 0x100000,
+ PS_DLL = unchecked ( (int) 0x80000000 ),
+ INPROC = INPROC_SERVER | INPROC_HANDLER,
+ SERVER = INPROC_SERVER | LOCAL_SERVER | REMOTE_SERVER,
+ ALL = SERVER | INPROC_HANDLER
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/ErrorCodes.cs b/NAudio/CoreAudioApi/Interfaces/ErrorCodes.cs
new file mode 100644
index 00000000..017db881
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/ErrorCodes.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ enum AudioClientErrors
+ {
+ ///
+ /// AUDCLNT_E_NOT_INITIALIZED
+ ///
+ NotInitialized = unchecked((int)0x88890001),
+ ///
+ /// AUDCLNT_E_UNSUPPORTED_FORMAT
+ ///
+ UnsupportedFormat = unchecked((int)0x88890008),
+ ///
+ /// AUDCLNT_E_DEVICE_IN_USE
+ ///
+ DeviceInUse = unchecked((int)0x8889000A),
+
+ }
+
+ static class ErrorCodes
+ {
+ // AUDCLNT_ERR(n) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, n)
+ // AUDCLNT_SUCCESS(n) MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_AUDCLNT, n)
+ const int SEVERITY_ERROR = 1;
+ const int FACILITY_AUDCLNT = 0x889;
+ static readonly int AUDCLNT_E_NOT_INITIALIZED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x001);
+ static readonly int AUDCLNT_E_ALREADY_INITIALIZED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x002);
+ static readonly int AUDCLNT_E_WRONG_ENDPOINT_TYPE = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x003);
+ static readonly int AUDCLNT_E_DEVICE_INVALIDATED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x004);
+ static readonly int AUDCLNT_E_NOT_STOPPED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x005);
+ static readonly int AUDCLNT_E_BUFFER_TOO_LARGE = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x006);
+ static readonly int AUDCLNT_E_OUT_OF_ORDER = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x007);
+ static readonly int AUDCLNT_E_UNSUPPORTED_FORMAT = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x008);
+ static readonly int AUDCLNT_E_INVALID_SIZE = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x009);
+ static readonly int AUDCLNT_E_DEVICE_IN_USE = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x00A);
+ static readonly int AUDCLNT_E_BUFFER_OPERATION_PENDING = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x00B);
+ static readonly int AUDCLNT_E_THREAD_NOT_REGISTERED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x00C);
+ static readonly int AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x00E);
+ static readonly int AUDCLNT_E_ENDPOINT_CREATE_FAILED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x00F);
+ static readonly int AUDCLNT_E_SERVICE_NOT_RUNNING = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x010);
+ static readonly int AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x011);
+ static readonly int AUDCLNT_E_EXCLUSIVE_MODE_ONLY = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x0012);
+ static readonly int AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x013);
+ static readonly int AUDCLNT_E_EVENTHANDLE_NOT_SET = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x014);
+ static readonly int AUDCLNT_E_INCORRECT_BUFFER_SIZE = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x015);
+ static readonly int AUDCLNT_E_BUFFER_SIZE_ERROR = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x016);
+ static readonly int AUDCLNT_E_CPUUSAGE_EXCEEDED = HResult.MAKE_HRESULT(SEVERITY_ERROR, FACILITY_AUDCLNT, 0x017);
+ /*static readonly int AUDCLNT_S_BUFFER_EMPTY AUDCLNT_SUCCESS(0x001)
+ static readonly int AUDCLNT_S_THREAD_ALREADY_REGISTERED AUDCLNT_SUCCESS(0x002)
+ static readonly int AUDCLNT_S_POSITION_STALLED AUDCLNT_SUCCESS(0x003)*/
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioCaptureClient.cs b/NAudio/CoreAudioApi/Interfaces/IAudioCaptureClient.cs
new file mode 100644
index 00000000..d8c04e69
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioCaptureClient.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+
+
+ [Guid("C8ADBD64-E71E-48a0-A4DE-185C395CD317"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IAudioCaptureClient
+ {
+ /*HRESULT GetBuffer(
+ BYTE** ppData,
+ UINT32* pNumFramesToRead,
+ DWORD* pdwFlags,
+ UINT64* pu64DevicePosition,
+ UINT64* pu64QPCPosition
+ );*/
+
+ int GetBuffer(
+ out IntPtr dataBuffer,
+ out int numFramesToRead,
+ out AudioClientBufferFlags bufferFlags,
+ out long devicePosition,
+ out long qpcPosition);
+
+ int ReleaseBuffer(int numFramesRead);
+
+ int GetNextPacketSize(out int numFramesInNextPacket);
+
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioClient.cs b/NAudio/CoreAudioApi/Interfaces/IAudioClient.cs
new file mode 100644
index 00000000..0efe6461
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioClient.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// Windows CoreAudio IAudioClient interface
+ /// Defined in AudioClient.h
+ ///
+ [Guid("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioClient
+ {
+ [PreserveSig]
+ int Initialize(AudioClientShareMode shareMode,
+ AudioClientStreamFlags StreamFlags,
+ long hnsBufferDuration, // REFERENCE_TIME
+ long hnsPeriodicity, // REFERENCE_TIME
+ [In] WaveFormat pFormat,
+ [In] ref Guid AudioSessionGuid);
+
+ ///
+ /// The GetBufferSize method retrieves the size (maximum capacity) of the endpoint buffer.
+ ///
+ int GetBufferSize(out uint bufferSize);
+
+ [return: MarshalAs(UnmanagedType.I8)]
+ long GetStreamLatency();
+
+ int GetCurrentPadding(out int currentPadding);
+
+ [PreserveSig]
+ int IsFormatSupported(
+ AudioClientShareMode shareMode,
+ [In] WaveFormat pFormat,
+ [Out, MarshalAs(UnmanagedType.LPStruct)] out WaveFormatExtensible closestMatchFormat);
+
+ int GetMixFormat(out IntPtr deviceFormatPointer);
+
+ // REFERENCE_TIME is 64 bit int
+ int GetDevicePeriod(out long defaultDevicePeriod, out long minimumDevicePeriod);
+
+ int Start();
+
+ int Stop();
+
+ int Reset();
+
+ int SetEventHandle(IntPtr eventHandle);
+
+ ///
+ /// The GetService method accesses additional services from the audio client object.
+ ///
+ /// The interface ID for the requested service.
+ /// Pointer to a pointer variable into which the method writes the address of an instance of the requested interface.
+ [PreserveSig]
+ int GetService([In, MarshalAs(UnmanagedType.LPStruct)] Guid interfaceId, [Out, MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioClock2.cs b/NAudio/CoreAudioApi/Interfaces/IAudioClock2.cs
new file mode 100644
index 00000000..951f78f9
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioClock2.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// Defined in AudioClient.h
+ ///
+ [Guid("CD63314F-3FBA-4a1b-812C-EF96358728E7"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioClock
+ {
+ [PreserveSig]
+ int GetFrequency(out ulong frequency);
+
+ [PreserveSig]
+ int GetPosition(out ulong devicePosition, out ulong qpcPosition);
+
+ [PreserveSig]
+ int GetCharacteristics(out uint characteristics);
+ }
+
+ ///
+ /// Defined in AudioClient.h
+ ///
+ [Guid("6f49ff73-6727-49AC-A008-D98CF5E70048"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioClock2 : IAudioClock
+ {
+ [PreserveSig]
+ int GetDevicePosition(out ulong devicePosition, out ulong qpcPosition);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioEndpointVolume.cs b/NAudio/CoreAudioApi/Interfaces/IAudioEndpointVolume.cs
new file mode 100644
index 00000000..8f0104fb
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioEndpointVolume.cs
@@ -0,0 +1,52 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioEndpointVolume
+ {
+ int RegisterControlChangeNotify(IAudioEndpointVolumeCallback pNotify);
+ int UnregisterControlChangeNotify(IAudioEndpointVolumeCallback pNotify);
+ int GetChannelCount(out int pnChannelCount);
+ int SetMasterVolumeLevel(float fLevelDB, Guid pguidEventContext);
+ int SetMasterVolumeLevelScalar(float fLevel, Guid pguidEventContext);
+ int GetMasterVolumeLevel(out float pfLevelDB);
+ int GetMasterVolumeLevelScalar(out float pfLevel);
+ int SetChannelVolumeLevel(uint nChannel, float fLevelDB, Guid pguidEventContext);
+ int SetChannelVolumeLevelScalar(uint nChannel, float fLevel, Guid pguidEventContext);
+ int GetChannelVolumeLevel(uint nChannel, out float pfLevelDB);
+ int GetChannelVolumeLevelScalar(uint nChannel, out float pfLevel);
+ int SetMute([MarshalAs(UnmanagedType.Bool)] Boolean bMute, Guid pguidEventContext);
+ int GetMute(out bool pbMute);
+ int GetVolumeStepInfo(out uint pnStep, out uint pnStepCount);
+ int VolumeStepUp(Guid pguidEventContext);
+ int VolumeStepDown(Guid pguidEventContext);
+ int QueryHardwareSupport(out uint pdwHardwareSupportMask);
+ int GetVolumeRange(out float pflVolumeMindB, out float pflVolumeMaxdB, out float pflVolumeIncrementdB);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioEndpointVolumeCallback.cs b/NAudio/CoreAudioApi/Interfaces/IAudioEndpointVolumeCallback.cs
new file mode 100644
index 00000000..6515a1a7
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioEndpointVolumeCallback.cs
@@ -0,0 +1,39 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ (Modified for NAudio by Mark Heath)
+
+*/
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ [Guid("657804FA-D6AD-4496-8A60-352752AF4F89"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioEndpointVolumeCallback
+ {
+ void OnNotify(IntPtr notifyData);
+ };
+
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioMeterInformation.cs b/NAudio/CoreAudioApi/Interfaces/IAudioMeterInformation.cs
new file mode 100644
index 00000000..6dbce42e
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioMeterInformation.cs
@@ -0,0 +1,36 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ [Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioMeterInformation
+ {
+ int GetPeakValue(out float pfPeak);
+ int GetMeteringChannelCount(out int pnChannelCount);
+ int GetChannelsPeakValues(int u32ChannelCount, [In] IntPtr afPeakValues);
+ int QueryHardwareSupport(out int pdwHardwareSupportMask);
+ };
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioRenderClient.cs b/NAudio/CoreAudioApi/Interfaces/IAudioRenderClient.cs
new file mode 100644
index 00000000..2ffbd845
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioRenderClient.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ [Guid("F294ACFC-3146-4483-A7BF-ADDCA7C260E2"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IAudioRenderClient
+ {
+ int GetBuffer(int numFramesRequested, out IntPtr dataBufferPointer);
+ int ReleaseBuffer(int numFramesWritten, AudioClientBufferFlags bufferFlags);
+ }
+
+
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioSessionControl.cs b/NAudio/CoreAudioApi/Interfaces/IAudioSessionControl.cs
new file mode 100644
index 00000000..1263acad
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioSessionControl.cs
@@ -0,0 +1,122 @@
+// -----------------------------------------
+// SoundScribe (TM) and related software.
+//
+// Copyright (C) 2007-2011 Vannatech
+// http://www.vannatech.com
+// All rights reserved.
+//
+// This source code is subject to the MIT License.
+// http://www.opensource.org/licenses/mit-license.php
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// -----------------------------------------
+// milligan22963 - ported to nAudio
+// -----------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// Windows CoreAudio IAudioSessionControl interface
+ /// Defined in AudioPolicy.h
+ ///
+ [Guid("F4B1A599-7266-4319-A8CA-E70ACB11E8CD"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioSessionControl
+ {
+ ///
+ /// Retrieves the current state of the audio session.
+ ///
+ /// Receives the current session state.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetState(
+ [Out] out AudioSessionState state);
+
+ ///
+ /// Retrieves the display name for the audio session.
+ ///
+ /// Receives a string that contains the display name.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetDisplayName(
+ [Out] [MarshalAs(UnmanagedType.LPWStr)] out string displayName);
+
+ ///
+ /// Assigns a display name to the current audio session.
+ ///
+ /// A string that contains the new display name for the session.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int SetDisplayName(
+ [In] [MarshalAs(UnmanagedType.LPWStr)] string displayName,
+ [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
+
+ ///
+ /// Retrieves the path for the display icon for the audio session.
+ ///
+ /// Receives a string that specifies the fully qualified path of the file that contains the icon.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetIconPath(
+ [Out] [MarshalAs(UnmanagedType.LPWStr)] out string iconPath);
+
+ ///
+ /// Assigns a display icon to the current session.
+ ///
+ /// A string that specifies the fully qualified path of the file that contains the new icon.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int SetIconPath(
+ [In] [MarshalAs(UnmanagedType.LPWStr)] string iconPath,
+ [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
+
+ ///
+ /// Retrieves the grouping parameter of the audio session.
+ ///
+ /// Receives the grouping parameter ID.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetGroupingParam(
+ [Out] out Guid groupingId);
+
+ ///
+ /// Assigns a session to a grouping of sessions.
+ ///
+ /// The new grouping parameter ID.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int SetGroupingParam(
+ [In] [MarshalAs(UnmanagedType.LPStruct)] Guid groupingId,
+ [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
+
+ ///
+ /// Registers the client to receive notifications of session events, including changes in the session state.
+ ///
+ /// A client-implemented interface.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int RegisterAudioSessionNotification(
+ [In] IAudioSessionEvents client);
+
+ ///
+ /// Deletes a previous registration by the client to receive notifications.
+ ///
+ /// A client-implemented interface.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int UnregisterAudioSessionNotification(
+ [In] IAudioSessionEvents client);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioSessionEvents.cs b/NAudio/CoreAudioApi/Interfaces/IAudioSessionEvents.cs
new file mode 100644
index 00000000..a95a7962
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioSessionEvents.cs
@@ -0,0 +1,177 @@
+// -----------------------------------------
+// SoundScribe (TM) and related software.
+//
+// Copyright (C) 2007-2011 Vannatech
+// http://www.vannatech.com
+// All rights reserved.
+//
+// This source code is subject to the MIT License.
+// http://www.opensource.org/licenses/mit-license.php
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// -----------------------------------------
+// milligan22963 - ported to nAudio
+// -----------------------------------------
+
+ using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// Defines constants that indicate the current state of an audio session.
+ ///
+ ///
+ /// MSDN Reference: http://msdn.microsoft.com/en-us/library/dd370792.aspx
+ ///
+ public enum AudioSessionState
+ {
+ ///
+ /// The audio session is inactive.
+ ///
+ AudioSessionStateInactive = 0,
+
+ ///
+ /// The audio session is active.
+ ///
+ AudioSessionStateActive = 1,
+
+ ///
+ /// The audio session has expired.
+ ///
+ AudioSessionStateExpired = 2
+ }
+
+ ///
+ /// Defines constants that indicate a reason for an audio session being disconnected.
+ ///
+ ///
+ /// MSDN Reference: Unknown
+ ///
+ public enum AudioSessionDisconnectReason
+ {
+ ///
+ /// The user removed the audio endpoint device.
+ ///
+ DisconnectReasonDeviceRemoval = 0,
+
+ ///
+ /// The Windows audio service has stopped.
+ ///
+ DisconnectReasonServerShutdown = 1,
+
+ ///
+ /// The stream format changed for the device that the audio session is connected to.
+ ///
+ DisconnectReasonFormatChanged = 2,
+
+ ///
+ /// The user logged off the WTS session that the audio session was running in.
+ ///
+ DisconnectReasonSessionLogoff = 3,
+
+ ///
+ /// The WTS session that the audio session was running in was disconnected.
+ ///
+ DisconnectReasonSessionDisconnected = 4,
+
+ ///
+ /// The (shared-mode) audio session was disconnected to make the audio endpoint device available for an exclusive-mode connection.
+ ///
+ DisconnectReasonExclusiveModeOverride = 5
+ }
+
+ ///
+ /// Windows CoreAudio IAudioSessionControl interface
+ /// Defined in AudioPolicy.h
+ ///
+ [Guid("24918ACC-64B3-37C1-8CA9-74A66E9957A8"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioSessionEvents
+ {
+ ///
+ /// Notifies the client that the display name for the session has changed.
+ ///
+ /// The new display name for the session.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int OnDisplayNameChanged(
+ [In] [MarshalAs(UnmanagedType.LPWStr)] string displayName,
+ [In] ref Guid eventContext);
+
+ ///
+ /// Notifies the client that the display icon for the session has changed.
+ ///
+ /// The path for the new display icon for the session.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int OnIconPathChanged(
+ [In] [MarshalAs(UnmanagedType.LPWStr)] string iconPath,
+ [In] ref Guid eventContext);
+
+ ///
+ /// Notifies the client that the volume level or muting state of the session has changed.
+ ///
+ /// The new volume level for the audio session.
+ /// The new muting state.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int OnSimpleVolumeChanged(
+ [In] [MarshalAs(UnmanagedType.R4)] float volume,
+ [In] [MarshalAs(UnmanagedType.Bool)] bool isMuted,
+ [In] ref Guid eventContext);
+
+ ///
+ /// Notifies the client that the volume level of an audio channel in the session submix has changed.
+ ///
+ /// The channel count.
+ /// An array of volumnes cooresponding with each channel index.
+ /// The number of the channel whose volume level changed.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int OnChannelVolumeChanged(
+ [In] [MarshalAs(UnmanagedType.U4)] UInt32 channelCount,
+ [In] [MarshalAs(UnmanagedType.SysInt)] IntPtr newVolumes, // Pointer to float array
+ [In] [MarshalAs(UnmanagedType.U4)] UInt32 channelIndex,
+ [In] ref Guid eventContext);
+
+ ///
+ /// Notifies the client that the grouping parameter for the session has changed.
+ ///
+ /// The new grouping parameter for the session.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int OnGroupingParamChanged(
+ [In] ref Guid groupingId,
+ [In] ref Guid eventContext);
+
+ ///
+ /// Notifies the client that the stream-activity state of the session has changed.
+ ///
+ /// The new session state.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int OnStateChanged(
+ [In] AudioSessionState state);
+
+ ///
+ /// Notifies the client that the session has been disconnected.
+ ///
+ /// The reason that the audio session was disconnected.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int OnSessionDisconnected(
+ [In] AudioSessionDisconnectReason disconnectReason);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioSessionEventsHandler.cs b/NAudio/CoreAudioApi/Interfaces/IAudioSessionEventsHandler.cs
new file mode 100644
index 00000000..5645dac0
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioSessionEventsHandler.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// interface to receive session related events
+ ///
+ public interface IAudioSessionEventsHandler
+ {
+ ///
+ /// notification of volume changes including muting of audio session
+ ///
+ /// the current volume
+ /// the current mute state, true muted, false otherwise
+ void OnVolumeChanged(float volume, bool isMuted);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IAudioSessionManager.cs b/NAudio/CoreAudioApi/Interfaces/IAudioSessionManager.cs
new file mode 100644
index 00000000..850486cb
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IAudioSessionManager.cs
@@ -0,0 +1,61 @@
+// -----------------------------------------
+// SoundScribe (TM) and related software.
+//
+// Copyright (C) 2007-2011 Vannatech
+// http://www.vannatech.com
+// All rights reserved.
+//
+// This source code is subject to the MIT License.
+// http://www.opensource.org/licenses/mit-license.php
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// -----------------------------------------
+// milligan22963 - ported to nAudio
+// -----------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// Windows CoreAudio IAudioSessionManager interface
+ /// Defined in AudioPolicy.h
+ ///
+ [Guid("BFA971F1-4D5E-40BB-935E-967039BFBEE4"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface IAudioSessionManager
+ {
+ ///
+ /// Retrieves an audio session control.
+ ///
+ /// A new or existing session ID.
+ /// Audio session flags.
+ /// Receives an interface for the audio session.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetAudioSessionControl(
+ [In, Optional] [MarshalAs(UnmanagedType.LPStruct)] Guid sessionId,
+ [In] [MarshalAs(UnmanagedType.U4)] UInt32 streamFlags,
+ [Out] [MarshalAs(UnmanagedType.Interface)] out IAudioSessionControl sessionControl);
+
+ ///
+ /// Retrieves a simple audio volume control.
+ ///
+ /// A new or existing session ID.
+ /// Audio session flags.
+ /// Receives an interface for the audio session.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetSimpleAudioVolume(
+ [In, Optional] [MarshalAs(UnmanagedType.LPStruct)] Guid sessionId,
+ [In] [MarshalAs(UnmanagedType.U4)] UInt32 streamFlags,
+ [Out] [MarshalAs(UnmanagedType.Interface)] out ISimpleAudioVolume audioVolume);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IMMDevice.cs b/NAudio/CoreAudioApi/Interfaces/IMMDevice.cs
new file mode 100644
index 00000000..b734b425
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IMMDevice.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ [Guid("D666063F-1587-4E43-81F1-B948E807363F"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IMMDevice
+ {
+ // activationParams is a propvariant
+ int Activate(ref Guid id, ClsCtx clsCtx, IntPtr activationParams,
+ [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer);
+
+ int OpenPropertyStore(StorageAccessMode stgmAccess, out IPropertyStore properties);
+
+ int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id);
+
+ int GetState(out DeviceState state);
+ }
+
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IMMDeviceCollection.cs b/NAudio/CoreAudioApi/Interfaces/IMMDeviceCollection.cs
new file mode 100644
index 00000000..6a9402a6
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IMMDeviceCollection.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IMMDeviceCollection
+ {
+ int GetCount(out int numDevices);
+ int Item(int deviceNumber, out IMMDevice device);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IMMDeviceEnumerator.cs b/NAudio/CoreAudioApi/Interfaces/IMMDeviceEnumerator.cs
new file mode 100644
index 00000000..e0b4c9dc
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IMMDeviceEnumerator.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IMMDeviceEnumerator
+ {
+ int EnumAudioEndpoints(DataFlow dataFlow, DeviceState stateMask,
+ out IMMDeviceCollection devices);
+
+ int GetDefaultAudioEndpoint(DataFlow dataFlow, Role role, out IMMDevice endpoint);
+
+ int GetDevice(string id, out IMMDevice deviceName);
+
+ int RegisterEndpointNotificationCallback(IMMNotificationClient client);
+
+ int UnregisterEndpointNotificationCallback(IMMNotificationClient client);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IMMEndpoint.cs b/NAudio/CoreAudioApi/Interfaces/IMMEndpoint.cs
new file mode 100644
index 00000000..4d7c8b66
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IMMEndpoint.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// defined in MMDeviceAPI.h
+ ///
+ [Guid("1BE09788-6894-4089-8586-9A2A6C265AC5"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IMMEndpoint
+ {
+ int GetDataFlow(out DataFlow dataFlow);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IMMNotificationClient.cs b/NAudio/CoreAudioApi/Interfaces/IMMNotificationClient.cs
new file mode 100644
index 00000000..91071854
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IMMNotificationClient.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// IMMNotificationClient
+ ///
+ [Guid("7991EEC9-7E89-4D85-8390-6C703CEC60C0"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IMMNotificationClient
+ {
+ ///
+ /// Device State Changed
+ ///
+ void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState);
+
+ ///
+ /// Device Added
+ ///
+ void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId);
+
+ ///
+ /// Device Removed
+ ///
+ void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId);
+
+ ///
+ /// Default Device Changed
+ ///
+ void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId);
+
+ ///
+ /// Property Value Changed
+ ///
+ ///
+ ///
+ void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key);
+ }
+
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/IPropertyStore.cs b/NAudio/CoreAudioApi/Interfaces/IPropertyStore.cs
new file mode 100644
index 00000000..4d6e8f35
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/IPropertyStore.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// is defined in propsys.h
+ ///
+ [Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IPropertyStore
+ {
+ int GetCount(out int propCount);
+ int GetAt(int property, out PropertyKey key);
+ int GetValue(ref PropertyKey key, out PropVariant value);
+ int SetValue(ref PropertyKey key, ref PropVariant value);
+ int Commit();
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/ISimpleAudioVolume.cs b/NAudio/CoreAudioApi/Interfaces/ISimpleAudioVolume.cs
new file mode 100644
index 00000000..42583c7a
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/ISimpleAudioVolume.cs
@@ -0,0 +1,75 @@
+// -----------------------------------------
+// SoundScribe (TM) and related software.
+//
+// Copyright (C) 2007-2011 Vannatech
+// http://www.vannatech.com
+// All rights reserved.
+//
+// This source code is subject to the MIT License.
+// http://www.opensource.org/licenses/mit-license.php
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+// -----------------------------------------
+// milligan22963 - ported to nAudio
+// -----------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// Windows CoreAudio ISimpleAudioVolume interface
+ /// Defined in AudioClient.h
+ ///
+ [Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ internal interface ISimpleAudioVolume
+ {
+ ///
+ /// Sets the master volume level for the audio session.
+ ///
+ /// The new volume level expressed as a normalized value between 0.0 and 1.0.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int SetMasterVolume(
+ [In] [MarshalAs(UnmanagedType.R4)] float levelNorm,
+ [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
+
+ ///
+ /// Retrieves the client volume level for the audio session.
+ ///
+ /// Receives the volume level expressed as a normalized value between 0.0 and 1.0.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetMasterVolume(
+ [Out] [MarshalAs(UnmanagedType.R4)] out float levelNorm);
+
+ ///
+ /// Sets the muting state for the audio session.
+ ///
+ /// The new muting state.
+ /// A user context value that is passed to the notification callback.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int SetMute(
+ [In] [MarshalAs(UnmanagedType.Bool)] bool isMuted,
+ [In] [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext);
+
+ ///
+ /// Retrieves the current muting state for the audio session.
+ ///
+ /// Receives the muting state.
+ /// An HRESULT code indicating whether the operation succeeded of failed.
+ [PreserveSig]
+ int GetMute(
+ [Out] [MarshalAs(UnmanagedType.Bool)] out bool isMuted);
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/MMDeviceEnumeratorComObject.cs b/NAudio/CoreAudioApi/Interfaces/MMDeviceEnumeratorComObject.cs
new file mode 100644
index 00000000..a144fe72
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/MMDeviceEnumeratorComObject.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// implements IMMDeviceEnumerator
+ ///
+ [ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")]
+ class MMDeviceEnumeratorComObject
+ {
+ }
+}
diff --git a/NAudio/CoreAudioApi/Interfaces/StorageAccessMode.cs b/NAudio/CoreAudioApi/Interfaces/StorageAccessMode.cs
new file mode 100644
index 00000000..1d01f02c
--- /dev/null
+++ b/NAudio/CoreAudioApi/Interfaces/StorageAccessMode.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// MMDevice STGM enumeration
+ ///
+ enum StorageAccessMode
+ {
+ Read,
+ Write,
+ ReadWrite
+ }
+}
diff --git a/NAudio/CoreAudioApi/MMDevice.cs b/NAudio/CoreAudioApi/MMDevice.cs
new file mode 100644
index 00000000..4738fe44
--- /dev/null
+++ b/NAudio/CoreAudioApi/MMDevice.cs
@@ -0,0 +1,258 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// modified for NAudio
+// milligan22963 - updated to include audio session manager
+
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// MM Device
+ ///
+ public class MMDevice
+ {
+ #region Variables
+ private readonly IMMDevice deviceInterface;
+ private PropertyStore propertyStore;
+ private AudioMeterInformation audioMeterInformation;
+ private AudioEndpointVolume audioEndpointVolume;
+ private AudioSessionManager audioSessionManager;
+ #endregion
+
+ #region Guids
+ private static Guid IID_IAudioMeterInformation = new Guid("C02216F6-8C67-4B5B-9D00-D008E73E0064");
+ private static Guid IID_IAudioEndpointVolume = new Guid("5CDF2C82-841E-4546-9722-0CF74078229A");
+ private static Guid IID_IAudioClient = new Guid("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2");
+ private static Guid IDD_IAudioSessionManager = new Guid("BFA971F1-4D5E-40BB-935E-967039BFBEE4");
+ #endregion
+
+ #region Init
+ private void GetPropertyInformation()
+ {
+ IPropertyStore propstore;
+ Marshal.ThrowExceptionForHR(deviceInterface.OpenPropertyStore(StorageAccessMode.Read, out propstore));
+ propertyStore = new PropertyStore(propstore);
+ }
+
+ private AudioClient GetAudioClient()
+ {
+ object result;
+ Marshal.ThrowExceptionForHR(deviceInterface.Activate(ref IID_IAudioClient, ClsCtx.ALL, IntPtr.Zero, out result));
+ return new AudioClient(result as IAudioClient);
+ }
+
+ private void GetAudioMeterInformation()
+ {
+ object result;
+ Marshal.ThrowExceptionForHR(deviceInterface.Activate(ref IID_IAudioMeterInformation, ClsCtx.ALL, IntPtr.Zero, out result));
+ audioMeterInformation = new AudioMeterInformation(result as IAudioMeterInformation);
+ }
+
+ private void GetAudioEndpointVolume()
+ {
+ object result;
+ Marshal.ThrowExceptionForHR(deviceInterface.Activate(ref IID_IAudioEndpointVolume, ClsCtx.ALL, IntPtr.Zero, out result));
+ audioEndpointVolume = new AudioEndpointVolume(result as IAudioEndpointVolume);
+ }
+
+ private void GetAudioSessionManager()
+ {
+ object result;
+ Marshal.ThrowExceptionForHR(deviceInterface.Activate(ref IDD_IAudioSessionManager, ClsCtx.ALL, IntPtr.Zero, out result));
+ audioSessionManager = new AudioSessionManager(result as IAudioSessionManager);
+ }
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Audio Client
+ ///
+ public AudioClient AudioClient
+ {
+ get
+ {
+ // now makes a new one each call to allow caller to manage when to dispose
+ // n.b. should probably not be a property anymore
+ return GetAudioClient();
+ }
+ }
+
+ ///
+ /// Audio Meter Information
+ ///
+ public AudioMeterInformation AudioMeterInformation
+ {
+ get
+ {
+ if (audioMeterInformation == null)
+ GetAudioMeterInformation();
+
+ return audioMeterInformation;
+ }
+ }
+
+ ///
+ /// Audio Endpoint Volume
+ ///
+ public AudioEndpointVolume AudioEndpointVolume
+ {
+ get
+ {
+ if (audioEndpointVolume == null)
+ GetAudioEndpointVolume();
+
+ return audioEndpointVolume;
+ }
+ }
+
+ ///
+ /// AudioSessionManager instance
+ ///
+ public AudioSessionManager AudioSessionManager
+ {
+ get
+ {
+ if (audioSessionManager == null)
+ {
+ GetAudioSessionManager();
+ }
+ return audioSessionManager;
+ }
+ }
+
+ ///
+ /// Properties
+ ///
+ public PropertyStore Properties
+ {
+ get
+ {
+ if (propertyStore == null)
+ GetPropertyInformation();
+ return propertyStore;
+ }
+ }
+
+ ///
+ /// Friendly name for the endpoint
+ ///
+ public string FriendlyName
+ {
+ get
+ {
+ if (propertyStore == null)
+ {
+ GetPropertyInformation();
+ }
+ if (propertyStore.Contains(PropertyKeys.PKEY_Device_FriendlyName))
+ {
+ return (string)propertyStore[PropertyKeys.PKEY_Device_FriendlyName].Value;
+ }
+ else
+ return "Unknown";
+ }
+ }
+
+ ///
+ /// Friendly name of device
+ ///
+ public string DeviceFriendlyName
+ {
+ get
+ {
+ if (propertyStore == null)
+ {
+ GetPropertyInformation();
+ }
+ if (propertyStore.Contains(PropertyKeys.PKEY_DeviceInterface_FriendlyName))
+ {
+ return (string)propertyStore[PropertyKeys.PKEY_DeviceInterface_FriendlyName].Value;
+ }
+ else
+ {
+ return "Unknown";
+ }
+ }
+ }
+
+ ///
+ /// Device ID
+ ///
+ public string ID
+ {
+ get
+ {
+ string result;
+ Marshal.ThrowExceptionForHR(deviceInterface.GetId(out result));
+ return result;
+ }
+ }
+
+ ///
+ /// Data Flow
+ ///
+ public DataFlow DataFlow
+ {
+ get
+ {
+ DataFlow result;
+ var ep = deviceInterface as IMMEndpoint;
+ ep.GetDataFlow(out result);
+ return result;
+ }
+ }
+
+ ///
+ /// Device State
+ ///
+ public DeviceState State
+ {
+ get
+ {
+ DeviceState result;
+ Marshal.ThrowExceptionForHR(deviceInterface.GetState(out result));
+ return result;
+ }
+ }
+ #endregion
+
+ #region Constructor
+ internal MMDevice(IMMDevice realDevice)
+ {
+ deviceInterface = realDevice;
+ }
+ #endregion
+
+ ///
+ /// To string
+ ///
+ public override string ToString()
+ {
+ return FriendlyName;
+ }
+
+ }
+}
diff --git a/NAudio/CoreAudioApi/MMDeviceCollection.cs b/NAudio/CoreAudioApi/MMDeviceCollection.cs
new file mode 100644
index 00000000..14dede86
--- /dev/null
+++ b/NAudio/CoreAudioApi/MMDeviceCollection.cs
@@ -0,0 +1,95 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// updated for NAudio
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Multimedia Device Collection
+ ///
+ public class MMDeviceCollection : IEnumerable
+ {
+ private IMMDeviceCollection _MMDeviceCollection;
+
+ ///
+ /// Device count
+ ///
+ public int Count
+ {
+ get
+ {
+ int result;
+ Marshal.ThrowExceptionForHR(_MMDeviceCollection.GetCount(out result));
+ return result;
+ }
+ }
+
+ ///
+ /// Get device by index
+ ///
+ /// Device index
+ /// Device at the specified index
+ public MMDevice this[int index]
+ {
+ get
+ {
+ IMMDevice result;
+ _MMDeviceCollection.Item(index, out result);
+ return new MMDevice(result);
+ }
+ }
+
+ internal MMDeviceCollection(IMMDeviceCollection parent)
+ {
+ _MMDeviceCollection = parent;
+ }
+
+ #region IEnumerable Members
+
+ ///
+ /// Get Enumerator
+ ///
+ /// Device enumerator
+ public IEnumerator GetEnumerator()
+ {
+ for (int index = 0; index < Count; index++)
+ {
+ yield return this[index];
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/CoreAudioApi/MMDeviceEnumerator.cs b/NAudio/CoreAudioApi/MMDeviceEnumerator.cs
new file mode 100644
index 00000000..89841b90
--- /dev/null
+++ b/NAudio/CoreAudioApi/MMDeviceEnumerator.cs
@@ -0,0 +1,109 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// updated for use in NAudio
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+
+ ///
+ /// MM Device Enumerator
+ ///
+ public class MMDeviceEnumerator
+ {
+ private readonly IMMDeviceEnumerator realEnumerator;
+
+ ///
+ /// Creates a new MM Device Enumerator
+ ///
+ public MMDeviceEnumerator()
+ {
+#if !NETFX_CORE
+ if (System.Environment.OSVersion.Version.Major < 6)
+ {
+ throw new NotSupportedException("This functionality is only supported on Windows Vista or newer.");
+ }
+#endif
+ realEnumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
+ }
+
+ ///
+ /// Enumerate Audio Endpoints
+ ///
+ /// Desired DataFlow
+ /// State Mask
+ /// Device Collection
+ public MMDeviceCollection EnumerateAudioEndPoints(DataFlow dataFlow, DeviceState dwStateMask)
+ {
+ IMMDeviceCollection result;
+ Marshal.ThrowExceptionForHR(realEnumerator.EnumAudioEndpoints(dataFlow, dwStateMask, out result));
+ return new MMDeviceCollection(result);
+ }
+
+ ///
+ /// Get Default Endpoint
+ ///
+ /// Data Flow
+ /// Role
+ /// Device
+ public MMDevice GetDefaultAudioEndpoint(DataFlow dataFlow, Role role)
+ {
+ IMMDevice device = null;
+ Marshal.ThrowExceptionForHR(((IMMDeviceEnumerator)realEnumerator).GetDefaultAudioEndpoint(dataFlow, role, out device));
+ return new MMDevice(device);
+ }
+
+ ///
+ /// Get device by ID
+ ///
+ /// Device ID
+ /// Device
+ public MMDevice GetDevice(string id)
+ {
+ IMMDevice device = null;
+ Marshal.ThrowExceptionForHR(((IMMDeviceEnumerator)realEnumerator).GetDevice(id, out device));
+ return new MMDevice(device);
+ }
+
+ ///
+ /// Registers a call back for Device Events
+ ///
+ /// Object implementing IMMNotificationClient type casted as IMMNotificationClient interface
+ ///
+ public int RegisterEndpointNotificationCallback([In] [MarshalAs(UnmanagedType.Interface)] IMMNotificationClient client)
+ {
+ return realEnumerator.RegisterEndpointNotificationCallback(client);
+ }
+
+ ///
+ /// Unregisters a call back for Device Events
+ ///
+ /// Object implementing IMMNotificationClient type casted as IMMNotificationClient interface
+ ///
+ public int UnregisterEndpointNotificationCallback([In] [MarshalAs(UnmanagedType.Interface)] IMMNotificationClient client)
+ {
+ return realEnumerator.UnregisterEndpointNotificationCallback(client);
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/PropVariant.cs b/NAudio/CoreAudioApi/PropVariant.cs
new file mode 100644
index 00000000..3316c4fa
--- /dev/null
+++ b/NAudio/CoreAudioApi/PropVariant.cs
@@ -0,0 +1,210 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// adapted for use in NAudio
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace NAudio.CoreAudioApi.Interfaces
+{
+ ///
+ /// from Propidl.h.
+ /// http://msdn.microsoft.com/en-us/library/aa380072(VS.85).aspx
+ /// contains a union so we have to do an explicit layout
+ ///
+ [StructLayout(LayoutKind.Explicit)]
+ public struct PropVariant
+ {
+ [FieldOffset(0)] private short vt;
+ [FieldOffset(2)] private short wReserved1;
+ [FieldOffset(4)] private short wReserved2;
+ [FieldOffset(6)] private short wReserved3;
+ [FieldOffset(8)] private sbyte cVal;
+ [FieldOffset(8)] private byte bVal;
+ [FieldOffset(8)] private short iVal;
+ [FieldOffset(8)] private ushort uiVal;
+ [FieldOffset(8)] private int lVal;
+ [FieldOffset(8)] private uint ulVal;
+ [FieldOffset(8)] private int intVal;
+ [FieldOffset(8)] private uint uintVal;
+ [FieldOffset(8)] private long hVal;
+ [FieldOffset(8)] private long uhVal;
+ [FieldOffset(8)] private float fltVal;
+ [FieldOffset(8)] private double dblVal;
+ [FieldOffset(8)] private bool boolVal;
+ [FieldOffset(8)] private int scode;
+ //CY cyVal;
+ [FieldOffset(8)] private DateTime date;
+ [FieldOffset(8)] private System.Runtime.InteropServices.ComTypes.FILETIME filetime;
+ //CLSID* puuid;
+ //CLIPDATA* pclipdata;
+ //BSTR bstrVal;
+ //BSTRBLOB bstrblobVal;
+ [FieldOffset(8)] private Blob blobVal;
+ //LPSTR pszVal;
+ [FieldOffset(8)] private IntPtr pointerValue; //LPWSTR
+ //IUnknown* punkVal;
+ /*IDispatch* pdispVal;
+ IStream* pStream;
+ IStorage* pStorage;
+ LPVERSIONEDSTREAM pVersionedStream;
+ LPSAFEARRAY parray;
+ CAC cac;
+ CAUB caub;
+ CAI cai;
+ CAUI caui;
+ CAL cal;
+ CAUL caul;
+ CAH cah;
+ CAUH cauh;
+ CAFLT caflt;
+ CADBL cadbl;
+ CABOOL cabool;
+ CASCODE cascode;
+ CACY cacy;
+ CADATE cadate;
+ CAFILETIME cafiletime;
+ CACLSID cauuid;
+ CACLIPDATA caclipdata;
+ CABSTR cabstr;
+ CABSTRBLOB cabstrblob;
+ CALPSTR calpstr;
+ CALPWSTR calpwstr;
+ CAPROPVARIANT capropvar;
+ CHAR* pcVal;
+ UCHAR* pbVal;
+ SHORT* piVal;
+ USHORT* puiVal;
+ LONG* plVal;
+ ULONG* pulVal;
+ INT* pintVal;
+ UINT* puintVal;
+ FLOAT* pfltVal;
+ DOUBLE* pdblVal;
+ VARIANT_BOOL* pboolVal;
+ DECIMAL* pdecVal;
+ SCODE* pscode;
+ CY* pcyVal;
+ DATE* pdate;
+ BSTR* pbstrVal;
+ IUnknown** ppunkVal;
+ IDispatch** ppdispVal;
+ LPSAFEARRAY* pparray;
+ PROPVARIANT* pvarVal;
+ */
+
+ ///
+ /// Creates a new PropVariant containing a long value
+ ///
+ public static PropVariant FromLong(long value)
+ {
+ return new PropVariant() {vt = (short) VarEnum.VT_I8, hVal = value};
+ }
+
+ ///
+ /// Helper method to gets blob data
+ ///
+ private byte[] GetBlob()
+ {
+ var blob = new byte[blobVal.Length];
+ Marshal.Copy(blobVal.Data, blob, 0, blob.Length);
+ return blob;
+ }
+
+ ///
+ /// Interprets a blob as an array of structs
+ ///
+ public T[] GetBlobAsArrayOf()
+ {
+ var blobByteLength = blobVal.Length;
+ var singleInstance = (T) Activator.CreateInstance(typeof (T));
+ var structSize = Marshal.SizeOf(singleInstance);
+ if (blobByteLength%structSize != 0)
+ {
+ throw new InvalidDataException(String.Format("Blob size {0} not a multiple of struct size {1}", blobByteLength, structSize));
+ }
+ var items = blobByteLength/structSize;
+ var array = new T[items];
+ for (int n = 0; n < items; n++)
+ {
+ array[n] = (T) Activator.CreateInstance(typeof (T));
+ Marshal.PtrToStructure(new IntPtr((long) blobVal.Data + n*structSize), array[n]);
+ }
+ return array;
+ }
+
+ ///
+ /// Gets the type of data in this PropVariant
+ ///
+ public VarEnum DataType
+ {
+ get { return (VarEnum) vt; }
+ }
+
+ ///
+ /// Property value
+ ///
+ public object Value
+ {
+ get
+ {
+ VarEnum ve = DataType;
+ switch (ve)
+ {
+ case VarEnum.VT_I1:
+ return bVal;
+ case VarEnum.VT_I2:
+ return iVal;
+ case VarEnum.VT_I4:
+ return lVal;
+ case VarEnum.VT_I8:
+ return hVal;
+ case VarEnum.VT_INT:
+ return iVal;
+ case VarEnum.VT_UI4:
+ return ulVal;
+ case VarEnum.VT_UI8:
+ return uhVal;
+ case VarEnum.VT_LPWSTR:
+ return Marshal.PtrToStringUni(pointerValue);
+ case VarEnum.VT_BLOB:
+ case VarEnum.VT_VECTOR | VarEnum.VT_UI1:
+ return GetBlob();
+ case VarEnum.VT_CLSID:
+ return (Guid)Marshal.PtrToStructure(pointerValue, typeof(Guid));
+ }
+ throw new NotImplementedException("PropVariant " + ve.ToString());
+ }
+ }
+
+ ///
+ /// allows freeing up memory, might turn this into a Dispose method?
+ ///
+ public void Clear()
+ {
+ PropVariantClear(ref this);
+ }
+
+ [DllImport("ole32.dll")]
+ private static extern int PropVariantClear(ref PropVariant pvar);
+ }
+}
diff --git a/NAudio/CoreAudioApi/PropertyKey.cs b/NAudio/CoreAudioApi/PropertyKey.cs
new file mode 100644
index 00000000..b03ed3ee
--- /dev/null
+++ b/NAudio/CoreAudioApi/PropertyKey.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// PROPERTYKEY is defined in wtypes.h
+ ///
+ public struct PropertyKey
+ {
+ ///
+ /// Format ID
+ ///
+ public Guid formatId;
+ ///
+ /// Property ID
+ ///
+ public int propertyId;
+ ///
+ ///
+ ///
+ ///
+ public PropertyKey(Guid formatId, int propertyId)
+ {
+ this.formatId = formatId;
+ this.propertyId = propertyId;
+ }
+ }
+}
diff --git a/NAudio/CoreAudioApi/PropertyKeys.cs b/NAudio/CoreAudioApi/PropertyKeys.cs
new file mode 100644
index 00000000..1fb5698f
--- /dev/null
+++ b/NAudio/CoreAudioApi/PropertyKeys.cs
@@ -0,0 +1,84 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Property Keys
+ ///
+ public static class PropertyKeys
+ {
+ ///
+ /// PKEY_DeviceInterface_FriendlyName
+ ///
+ public static readonly PropertyKey PKEY_DeviceInterface_FriendlyName = new PropertyKey(new Guid(0x026e516e, unchecked((short)0xb814), 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22), 2);
+ ///
+ /// PKEY_AudioEndpoint_FormFactor
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_FormFactor = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 0);
+ ///
+ /// PKEY_AudioEndpoint_ControlPanelPageProvider
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_ControlPanelPageProvider = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 1);
+ ///
+ /// PKEY_AudioEndpoint_Association
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_Association = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 2);
+ ///
+ /// PKEY_AudioEndpoint_PhysicalSpeakers
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_PhysicalSpeakers = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 3);
+ ///
+ /// PKEY_AudioEndpoint_GUID
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_GUID = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 4);
+ ///
+ /// PKEY_AudioEndpoint_Disable_SysFx
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_Disable_SysFx = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 5);
+ ///
+ /// PKEY_AudioEndpoint_FullRangeSpeakers
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_FullRangeSpeakers = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 6);
+ ///
+ /// PKEY_AudioEndpoint_Supports_EventDriven_Mode
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_Supports_EventDriven_Mode = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 7);
+ ///
+ /// PKEY_AudioEndpoint_JackSubType
+ ///
+ public static readonly PropertyKey PKEY_AudioEndpoint_JackSubType = new PropertyKey(new Guid(0x1da5d803, unchecked((short)0xd492), 0x4edd, 0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e), 8);
+ ///
+ /// PKEY_AudioEngine_DeviceFormat
+ ///
+ public static readonly PropertyKey PKEY_AudioEngine_DeviceFormat = new PropertyKey(new Guid(unchecked((int)0xf19f064d), 0x82c, 0x4e27, 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c), 0);
+ ///
+ /// PKEY_AudioEngine_OEMFormat
+ ///
+ public static readonly PropertyKey PKEY_AudioEngine_OEMFormat = new PropertyKey(new Guid(unchecked((int)0xe4870e26), 0x3cc5, 0x4cd2, 0xba, 0x46, 0xca, 0xa, 0x9a, 0x70, 0xed, 0x4), 3);
+ ///
+ /// PKEY _Devie_FriendlyName
+ ///
+ public static readonly PropertyKey PKEY_Device_FriendlyName = new PropertyKey(new Guid(unchecked((int)0xa45c254e), unchecked((short)0xdf1c), 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 14);
+ }
+}
diff --git a/NAudio/CoreAudioApi/PropertyStore.cs b/NAudio/CoreAudioApi/PropertyStore.cs
new file mode 100644
index 00000000..d8bf754d
--- /dev/null
+++ b/NAudio/CoreAudioApi/PropertyStore.cs
@@ -0,0 +1,141 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// this version modified for NAudio from Ray Molenkamp's original
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Property Store class, only supports reading properties at the moment.
+ ///
+ public class PropertyStore
+ {
+ private readonly IPropertyStore storeInterface;
+
+ ///
+ /// Property Count
+ ///
+ public int Count
+ {
+ get
+ {
+ int result;
+ Marshal.ThrowExceptionForHR(storeInterface.GetCount(out result));
+ return result;
+ }
+ }
+
+ ///
+ /// Gets property by index
+ ///
+ /// Property index
+ /// The property
+ public PropertyStoreProperty this[int index]
+ {
+ get
+ {
+ PropVariant result;
+ PropertyKey key = Get(index);
+ Marshal.ThrowExceptionForHR(storeInterface.GetValue(ref key, out result));
+ return new PropertyStoreProperty(key, result);
+ }
+ }
+
+ ///
+ /// Contains property guid
+ ///
+ /// Looks for a specific key
+ /// True if found
+ public bool Contains(PropertyKey key)
+ {
+ for (int i = 0; i < Count; i++)
+ {
+ PropertyKey ikey = Get(i);
+ if ((ikey.formatId == key.formatId) && (ikey.propertyId == key.propertyId))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Indexer by guid
+ ///
+ /// Property Key
+ /// Property or null if not found
+ public PropertyStoreProperty this[PropertyKey key]
+ {
+ get
+ {
+ PropVariant result;
+ for (int i = 0; i < Count; i++)
+ {
+ PropertyKey ikey = Get(i);
+ if ((ikey.formatId == key.formatId) && (ikey.propertyId == key.propertyId))
+ {
+ Marshal.ThrowExceptionForHR(storeInterface.GetValue(ref ikey, out result));
+ return new PropertyStoreProperty(ikey, result);
+ }
+ }
+ return null;
+ }
+ }
+
+ ///
+ /// Gets property key at sepecified index
+ ///
+ /// Index
+ /// Property key
+ public PropertyKey Get(int index)
+ {
+ PropertyKey key;
+ Marshal.ThrowExceptionForHR(storeInterface.GetAt(index, out key));
+ return key;
+ }
+
+ ///
+ /// Gets property value at specified index
+ ///
+ /// Index
+ /// Property value
+ public PropVariant GetValue(int index)
+ {
+ PropVariant result;
+ PropertyKey key = Get(index);
+ Marshal.ThrowExceptionForHR(storeInterface.GetValue(ref key, out result));
+ return result;
+ }
+
+ ///
+ /// Creates a new property store
+ ///
+ /// IPropertyStore COM interface
+ internal PropertyStore(IPropertyStore store)
+ {
+ this.storeInterface = store;
+ }
+ }
+}
+
diff --git a/NAudio/CoreAudioApi/PropertyStoreProperty.cs b/NAudio/CoreAudioApi/PropertyStoreProperty.cs
new file mode 100644
index 00000000..9abf9425
--- /dev/null
+++ b/NAudio/CoreAudioApi/PropertyStoreProperty.cs
@@ -0,0 +1,61 @@
+/*
+ LICENSE
+ -------
+ Copyright (C) 2007 Ray Molenkamp
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this source code or the software it produces.
+
+ Permission is granted to anyone to use this source code for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+// modified from Ray Molenkamp's original
+
+
+using System;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Property Store Property
+ ///
+ public class PropertyStoreProperty
+ {
+ private readonly PropertyKey propertyKey;
+ private PropVariant propertyValue;
+
+ internal PropertyStoreProperty(PropertyKey key, PropVariant value)
+ {
+ propertyKey = key;
+ propertyValue = value;
+ }
+
+ ///
+ /// Property Key
+ ///
+ public PropertyKey Key
+ {
+ get { return propertyKey; }
+ }
+
+ ///
+ /// Property Value
+ ///
+ public object Value
+ {
+ get { return propertyValue.Value; }
+ }
+ }
+}
+
diff --git a/NAudio/CoreAudioApi/Role.cs b/NAudio/CoreAudioApi/Role.cs
new file mode 100644
index 00000000..60435a34
--- /dev/null
+++ b/NAudio/CoreAudioApi/Role.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// The ERole enumeration defines constants that indicate the role
+ /// that the system has assigned to an audio endpoint device
+ ///
+ public enum Role
+ {
+ ///
+ /// Games, system notification sounds, and voice commands.
+ ///
+ Console,
+
+ ///
+ /// Music, movies, narration, and live music recording
+ ///
+ Multimedia,
+
+ ///
+ /// Voice communications (talking to another person).
+ ///
+ Communications,
+ }
+}
diff --git a/NAudio/CoreAudioApi/SimpleAudioVolume.cs b/NAudio/CoreAudioApi/SimpleAudioVolume.cs
new file mode 100644
index 00000000..f2c77d4c
--- /dev/null
+++ b/NAudio/CoreAudioApi/SimpleAudioVolume.cs
@@ -0,0 +1,89 @@
+// -----------------------------------------
+// milligan22963 - implemented to work with nAudio
+// 12/2014
+// -----------------------------------------
+
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Windows CoreAudio SimpleAudioVolume
+ ///
+ public class SimpleAudioVolume : IDisposable
+ {
+ private readonly ISimpleAudioVolume simpleAudioVolume;
+
+ ///
+ /// Creates a new Audio endpoint volume
+ ///
+ /// ISimpleAudioVolume COM interface
+ internal SimpleAudioVolume(ISimpleAudioVolume realSimpleVolume)
+ {
+ simpleAudioVolume = realSimpleVolume;
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ~SimpleAudioVolume()
+ {
+ Dispose();
+ }
+
+ #endregion
+
+ ///
+ /// Allows the user to adjust the volume from
+ /// 0.0 to 1.0
+ ///
+ public float Volume
+ {
+ get
+ {
+ float result;
+ Marshal.ThrowExceptionForHR(simpleAudioVolume.GetMasterVolume(out result));
+ return result;
+ }
+ set
+ {
+ if ((value >= 0.0) && (value <= 1.0))
+ {
+ Marshal.ThrowExceptionForHR(simpleAudioVolume.SetMasterVolume(value, Guid.Empty));
+ }
+ // should throw something if out of range
+ }
+ }
+
+ ///
+ /// Mute
+ ///
+ public bool Mute
+ {
+ get
+ {
+ bool result;
+
+ Marshal.ThrowExceptionForHR(simpleAudioVolume.GetMute(out result));
+
+ return result;
+ }
+ set
+ {
+ Marshal.ThrowExceptionForHR(simpleAudioVolume.SetMute(value, Guid.Empty));
+ }
+ }
+ }
+}
diff --git a/NAudio/Dmo/AudioMediaSubtypes.cs b/NAudio/Dmo/AudioMediaSubtypes.cs
new file mode 100644
index 00000000..5aa9df1f
--- /dev/null
+++ b/NAudio/Dmo/AudioMediaSubtypes.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ class AudioMediaSubtypes
+ {
+ public static readonly Guid MEDIASUBTYPE_PCM = new Guid("00000001-0000-0010-8000-00AA00389B71"); // PCM audio.
+ public static readonly Guid MEDIASUBTYPE_PCMAudioObsolete = new Guid("e436eb8a-524f-11ce-9f53-0020af0ba770"); // Obsolete. Do not use.
+ public static readonly Guid MEDIASUBTYPE_MPEG1Packet = new Guid("e436eb80-524f-11ce-9f53-0020af0ba770"); // MPEG1 Audio packet.
+ public static readonly Guid MEDIASUBTYPE_MPEG1Payload = new Guid("e436eb81-524f-11ce-9f53-0020af0ba770"); // MPEG1 Audio Payload.
+ public static readonly Guid MEDIASUBTYPE_MPEG2_AUDIO = new Guid("e06d802b-db46-11cf-b4d1-00805f6cbbea"); // MPEG-2 audio data
+ public static readonly Guid MEDIASUBTYPE_DVD_LPCM_AUDIO = new Guid("e06d8032-db46-11cf-b4d1-00805f6cbbea"); // DVD audio data
+ public static readonly Guid MEDIASUBTYPE_DRM_Audio = new Guid("00000009-0000-0010-8000-00aa00389b71"); // Corresponds to WAVE_FORMAT_DRM.
+ public static readonly Guid MEDIASUBTYPE_IEEE_FLOAT = new Guid("00000003-0000-0010-8000-00aa00389b71"); // Corresponds to WAVE_FORMAT_IEEE_FLOAT
+ public static readonly Guid MEDIASUBTYPE_DOLBY_AC3 = new Guid("e06d802c-db46-11cf-b4d1-00805f6cbbea"); // Dolby data
+ public static readonly Guid MEDIASUBTYPE_DOLBY_AC3_SPDIF = new Guid("00000092-0000-0010-8000-00aa00389b71"); // Dolby AC3 over SPDIF.
+ public static readonly Guid MEDIASUBTYPE_RAW_SPORT = new Guid("00000240-0000-0010-8000-00aa00389b71"); // Equivalent to MEDIASUBTYPE_DOLBY_AC3_SPDIF.
+ public static readonly Guid MEDIASUBTYPE_SPDIF_TAG_241h = new Guid("00000241-0000-0010-8000-00aa00389b71"); // Equivalent to MEDIASUBTYPE_DOLBY_AC3_SPDIF.
+ //http://msdn.microsoft.com/en-us/library/dd757532%28VS.85%29.aspx
+ public static readonly Guid WMMEDIASUBTYPE_MP3 = new Guid("00000055-0000-0010-8000-00AA00389B71");
+ // others?
+ public static readonly Guid MEDIASUBTYPE_WAVE = new Guid("e436eb8b-524f-11ce-9f53-0020af0ba770");
+ public static readonly Guid MEDIASUBTYPE_AU = new Guid("e436eb8c-524f-11ce-9f53-0020af0ba770");
+ public static readonly Guid MEDIASUBTYPE_AIFF = new Guid("e436eb8d-524f-11ce-9f53-0020af0ba770");
+
+ public static readonly Guid[] AudioSubTypes = new Guid[]
+ {
+ MEDIASUBTYPE_PCM,
+ MEDIASUBTYPE_PCMAudioObsolete,
+ MEDIASUBTYPE_MPEG1Packet,
+ MEDIASUBTYPE_MPEG1Payload,
+ MEDIASUBTYPE_MPEG2_AUDIO,
+ MEDIASUBTYPE_DVD_LPCM_AUDIO,
+ MEDIASUBTYPE_DRM_Audio,
+ MEDIASUBTYPE_IEEE_FLOAT,
+ MEDIASUBTYPE_DOLBY_AC3,
+ MEDIASUBTYPE_DOLBY_AC3_SPDIF,
+ MEDIASUBTYPE_RAW_SPORT,
+ MEDIASUBTYPE_SPDIF_TAG_241h,
+ WMMEDIASUBTYPE_MP3,
+ };
+
+ public static readonly string[] AudioSubTypeNames = new string[]
+ {
+ "PCM",
+ "PCM Obsolete",
+ "MPEG1Packet",
+ "MPEG1Payload",
+ "MPEG2_AUDIO",
+ "DVD_LPCM_AUDIO",
+ "DRM_Audio",
+ "IEEE_FLOAT",
+ "DOLBY_AC3",
+ "DOLBY_AC3_SPDIF",
+ "RAW_SPORT",
+ "SPDIF_TAG_241h",
+ "MP3"
+ };
+ public static string GetAudioSubtypeName(Guid subType)
+ {
+ for (int index = 0; index < AudioSubTypes.Length; index++)
+ {
+ if (subType == AudioSubTypes[index])
+ {
+ return AudioSubTypeNames[index];
+ }
+ }
+ return subType.ToString();
+ }
+ }
+}
diff --git a/NAudio/Dmo/DmoDescriptor.cs b/NAudio/Dmo/DmoDescriptor.cs
new file mode 100644
index 00000000..b0c2c06a
--- /dev/null
+++ b/NAudio/Dmo/DmoDescriptor.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// Contains the name and CLSID of a DirectX Media Object
+ ///
+ public class DmoDescriptor
+ {
+ ///
+ /// Name
+ ///
+ public string Name { get; private set; }
+
+ ///
+ /// Clsid
+ ///
+ public Guid Clsid { get; private set; }
+
+ ///
+ /// Initializes a new instance of DmoDescriptor
+ ///
+ public DmoDescriptor(string name, Guid clsid)
+ {
+ this.Name = name;
+ this.Clsid = clsid;
+ }
+ }
+}
diff --git a/NAudio/Dmo/DmoEnumFlags.cs b/NAudio/Dmo/DmoEnumFlags.cs
new file mode 100644
index 00000000..394465ef
--- /dev/null
+++ b/NAudio/Dmo/DmoEnumFlags.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ [Flags]
+ enum DmoEnumFlags
+ {
+ None,
+ DMO_ENUMF_INCLUDE_KEYED = 0x00000001
+ }
+}
diff --git a/NAudio/Dmo/DmoEnumerator.cs b/NAudio/Dmo/DmoEnumerator.cs
new file mode 100644
index 00000000..0dbca00a
--- /dev/null
+++ b/NAudio/Dmo/DmoEnumerator.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// DirectX Media Object Enumerator
+ ///
+ public class DmoEnumerator
+ {
+ ///
+ /// Get audio effect names
+ ///
+ /// Audio effect names
+ public static IEnumerable GetAudioEffectNames()
+ {
+ return GetDmos(DmoGuids.DMOCATEGORY_AUDIO_EFFECT);
+ }
+
+ ///
+ /// Get audio encoder names
+ ///
+ /// Audio encoder names
+ public static IEnumerable GetAudioEncoderNames()
+ {
+ return GetDmos(DmoGuids.DMOCATEGORY_AUDIO_ENCODER);
+ }
+
+ ///
+ /// Get audio decoder names
+ ///
+ /// Audio decoder names
+ public static IEnumerable GetAudioDecoderNames()
+ {
+ return GetDmos(DmoGuids.DMOCATEGORY_AUDIO_DECODER);
+ }
+
+ private static IEnumerable GetDmos(Guid category)
+ {
+ IEnumDmo enumDmo;
+ int hresult = DmoInterop.DMOEnum(ref category, DmoEnumFlags.None, 0, null, 0, null, out enumDmo);
+ Marshal.ThrowExceptionForHR(hresult);
+ Guid guid;
+ int itemsFetched;
+ IntPtr namePointer;
+ do
+ {
+ enumDmo.Next(1, out guid, out namePointer, out itemsFetched);
+
+ if (itemsFetched == 1)
+ {
+ string name = Marshal.PtrToStringUni(namePointer);
+ Marshal.FreeCoTaskMem(namePointer);
+ yield return new DmoDescriptor(name, guid);
+ }
+ } while (itemsFetched > 0);
+ }
+ }
+}
diff --git a/NAudio/Dmo/DmoGuids.cs b/NAudio/Dmo/DmoGuids.cs
new file mode 100644
index 00000000..adb16002
--- /dev/null
+++ b/NAudio/Dmo/DmoGuids.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// DMO Guids for use with DMOEnum
+ /// dmoreg.h
+ ///
+ static class DmoGuids
+ {
+ public static readonly Guid DMOCATEGORY_AUDIO_DECODER = new Guid("57f2db8b-e6bb-4513-9d43-dcd2a6593125");
+ public static readonly Guid DMOCATEGORY_AUDIO_ENCODER = new Guid("33D9A761-90C8-11d0-BD43-00A0C911CE86");
+ public static readonly Guid DMOCATEGORY_VIDEO_DECODER = new Guid("4a69b442-28be-4991-969c-b500adf5d8a8");
+ public static readonly Guid DMOCATEGORY_VIDEO_ENCODER = new Guid("33D9A760-90C8-11d0-BD43-00A0C911CE86");
+ public static readonly Guid DMOCATEGORY_AUDIO_EFFECT = new Guid("f3602b3f-0592-48df-a4cd-674721e7ebeb");
+ public static readonly Guid DMOCATEGORY_VIDEO_EFFECT = new Guid("d990ee14-776c-4723-be46-3da2f56f10b9");
+ public static readonly Guid DMOCATEGORY_AUDIO_CAPTURE_EFFECT = new Guid("f665aaba-3e09-4920-aa5f-219811148f09");
+ }
+
+ static class DmoMediaTypeGuids
+ {
+ public static readonly Guid FORMAT_None = new Guid("0F6417D6-C318-11D0-A43F-00A0C9223196");
+ public static readonly Guid FORMAT_VideoInfo = new Guid("05589f80-c356-11ce-bf01-00aa0055595a");
+ public static readonly Guid FORMAT_VideoInfo2 = new Guid("F72A76A0-EB0A-11d0-ACE4-0000C0CC16BA");
+ public static readonly Guid FORMAT_WaveFormatEx = new Guid("05589f81-c356-11ce-bf01-00aa0055595a");
+ public static readonly Guid FORMAT_MPEGVideo = new Guid("05589f82-c356-11ce-bf01-00aa0055595a");
+ public static readonly Guid FORMAT_MPEGStreams = new Guid("05589f83-c356-11ce-bf01-00aa0055595a");
+ public static readonly Guid FORMAT_DvInfo = new Guid("05589f84-c356-11ce-bf01-00aa0055595a");
+ public static readonly Guid FORMAT_525WSS = new Guid("C7ECF04D-4582-4869-9ABB-BFB523B62EDF");
+ }
+}
diff --git a/NAudio/Dmo/DmoHResults.cs b/NAudio/Dmo/DmoHResults.cs
new file mode 100644
index 00000000..9eb893d1
--- /dev/null
+++ b/NAudio/Dmo/DmoHResults.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// MediaErr.h
+ ///
+ enum DmoHResults
+ {
+ DMO_E_INVALIDSTREAMINDEX = unchecked((int)0x80040201),
+ DMO_E_INVALIDTYPE = unchecked((int)0x80040202),
+ DMO_E_TYPE_NOT_SET = unchecked((int)0x80040203),
+ DMO_E_NOTACCEPTING = unchecked((int)0x80040204),
+ DMO_E_TYPE_NOT_ACCEPTED = unchecked((int)0x80040205),
+ DMO_E_NO_MORE_ITEMS = unchecked((int)0x80040206),
+ }
+}
diff --git a/NAudio/Dmo/DmoInputDataBufferFlags.cs b/NAudio/Dmo/DmoInputDataBufferFlags.cs
new file mode 100644
index 00000000..53875e94
--- /dev/null
+++ b/NAudio/Dmo/DmoInputDataBufferFlags.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// DMO Input Data Buffer Flags
+ ///
+ [Flags]
+ public enum DmoInputDataBufferFlags
+ {
+ ///
+ /// None
+ ///
+ None,
+ ///
+ /// DMO_INPUT_DATA_BUFFERF_SYNCPOINT
+ ///
+ SyncPoint = 0x00000001,
+ ///
+ /// DMO_INPUT_DATA_BUFFERF_TIME
+ ///
+ Time = 0x00000002,
+ ///
+ /// DMO_INPUT_DATA_BUFFERF_TIMELENGTH
+ ///
+ TimeLength = 0x00000004
+ }
+}
diff --git a/NAudio/Dmo/DmoInputStatusFlags.cs b/NAudio/Dmo/DmoInputStatusFlags.cs
new file mode 100644
index 00000000..ad7dc843
--- /dev/null
+++ b/NAudio/Dmo/DmoInputStatusFlags.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ [Flags]
+ enum DmoInputStatusFlags
+ {
+ None,
+ DMO_INPUT_STATUSF_ACCEPT_DATA = 0x1
+ }
+}
diff --git a/NAudio/Dmo/DmoInterop.cs b/NAudio/Dmo/DmoInterop.cs
new file mode 100644
index 00000000..ac0747d9
--- /dev/null
+++ b/NAudio/Dmo/DmoInterop.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ static class DmoInterop
+ {
+ [DllImport("msdmo.dll")]
+ public static extern int DMOEnum(
+ [In] ref Guid guidCategory,
+ DmoEnumFlags flags,
+ int inTypes,
+ [In] DmoPartialMediaType[] inTypesArray,
+ int outTypes,
+ [In] DmoPartialMediaType[] outTypesArray,
+ out IEnumDmo enumDmo);
+
+ [DllImport("msdmo.dll")]
+ public static extern int MoFreeMediaType(
+ [In] ref DmoMediaType mediaType);
+
+ [DllImport("msdmo.dll")]
+ public static extern int MoInitMediaType(
+ [In,Out] ref DmoMediaType mediaType, int formatBlockBytes);
+
+ [DllImport("msdmo.dll")]
+ public static extern int DMOGetName([In] ref Guid clsidDMO,
+ // preallocate 80 characters
+ [Out] StringBuilder name);
+ }
+}
diff --git a/NAudio/Dmo/DmoMediaType.cs b/NAudio/Dmo/DmoMediaType.cs
new file mode 100644
index 00000000..34a63ece
--- /dev/null
+++ b/NAudio/Dmo/DmoMediaType.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// http://msdn.microsoft.com/en-us/library/aa929922.aspx
+ /// DMO_MEDIA_TYPE
+ ///
+ public struct DmoMediaType
+ {
+ Guid majortype;
+ Guid subtype;
+ bool bFixedSizeSamples;
+ bool bTemporalCompression;
+ int lSampleSize;
+ Guid formattype;
+ IntPtr pUnk; // not used
+ int cbFormat;
+ IntPtr pbFormat;
+
+ ///
+ /// Major type
+ ///
+ public Guid MajorType
+ {
+ get { return majortype; }
+ }
+
+ ///
+ /// Major type name
+ ///
+ public string MajorTypeName
+ {
+ get { return MediaTypes.GetMediaTypeName(majortype); }
+ }
+
+ ///
+ /// Subtype
+ ///
+ public Guid SubType
+ {
+ get { return subtype; }
+ }
+
+ ///
+ /// Subtype name
+ ///
+ public string SubTypeName
+ {
+ get
+ {
+ if (majortype == MediaTypes.MEDIATYPE_Audio)
+ {
+ return AudioMediaSubtypes.GetAudioSubtypeName(subtype);
+ }
+ return subtype.ToString();
+ }
+ }
+
+ ///
+ /// Fixed size samples
+ ///
+ public bool FixedSizeSamples
+ {
+ get { return bFixedSizeSamples; }
+ }
+
+ ///
+ /// Sample size
+ ///
+ public int SampleSize
+ {
+ get { return lSampleSize; }
+ }
+
+ ///
+ /// Format type
+ ///
+ public Guid FormatType
+ {
+ get { return formattype; }
+ }
+
+ ///
+ /// Format type name
+ ///
+ public string FormatTypeName
+ {
+ get
+ {
+ if(formattype == DmoMediaTypeGuids.FORMAT_None)
+ {
+ return "None";
+ }
+ else if (formattype == Guid.Empty)
+ {
+ return "Null";
+ }
+ else if(formattype == DmoMediaTypeGuids.FORMAT_WaveFormatEx)
+ {
+ return "WaveFormatEx";
+ }
+ else
+ {
+ return FormatType.ToString();
+ }
+ }
+ }
+
+ ///
+ /// Gets the structure as a Wave format (if it is one)
+ ///
+ public WaveFormat GetWaveFormat()
+ {
+ if (formattype == DmoMediaTypeGuids.FORMAT_WaveFormatEx)
+ {
+ return WaveFormat.MarshalFromPtr(pbFormat);
+ }
+ else
+ {
+ throw new InvalidOperationException("Not a WaveFormat type");
+ }
+ }
+
+ ///
+ /// Sets this object up to point to a wave format
+ ///
+ /// Wave format structure
+ public void SetWaveFormat(WaveFormat waveFormat)
+ {
+ this.majortype = MediaTypes.MEDIATYPE_Audio;
+
+ WaveFormatExtensible wfe = waveFormat as WaveFormatExtensible;
+ if (wfe != null)
+ {
+ this.subtype = wfe.SubFormat;
+ }
+ else
+ {
+ switch (waveFormat.Encoding)
+ {
+ case WaveFormatEncoding.Pcm:
+ subtype = AudioMediaSubtypes.MEDIASUBTYPE_PCM;
+ break;
+ case WaveFormatEncoding.IeeeFloat:
+ subtype = AudioMediaSubtypes.MEDIASUBTYPE_IEEE_FLOAT;
+ break;
+ case WaveFormatEncoding.MpegLayer3:
+ subtype = AudioMediaSubtypes.WMMEDIASUBTYPE_MP3;
+ break;
+ default:
+ throw new ArgumentException(String.Format("Not a supported encoding {0}", waveFormat.Encoding));
+ }
+ }
+ this.bFixedSizeSamples = (this.SubType == AudioMediaSubtypes.MEDIASUBTYPE_PCM || this.SubType == AudioMediaSubtypes.MEDIASUBTYPE_IEEE_FLOAT);
+ this.formattype = DmoMediaTypeGuids.FORMAT_WaveFormatEx;
+ if (cbFormat < Marshal.SizeOf(waveFormat))
+ throw new InvalidOperationException("Not enough memory assigned for a WaveFormat structure");
+ //Debug.Assert(cbFormat >= ,"Not enough space");
+ Marshal.StructureToPtr(waveFormat, pbFormat, false);
+ }
+ }
+}
diff --git a/NAudio/Dmo/DmoOutputDataBuffer.cs b/NAudio/Dmo/DmoOutputDataBuffer.cs
new file mode 100644
index 00000000..6a371d64
--- /dev/null
+++ b/NAudio/Dmo/DmoOutputDataBuffer.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// DMO Output Data Buffer
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack=8)]
+ public struct DmoOutputDataBuffer : IDisposable
+ {
+ [MarshalAs(UnmanagedType.Interface)]
+ IMediaBuffer pBuffer;
+ DmoOutputDataBufferFlags dwStatus;
+ long rtTimestamp;
+ long referenceTimeDuration;
+
+ ///
+ /// Creates a new DMO Output Data Buffer structure
+ ///
+ /// Maximum buffer size
+ public DmoOutputDataBuffer(int maxBufferSize)
+ {
+ pBuffer = new MediaBuffer(maxBufferSize);
+ dwStatus = DmoOutputDataBufferFlags.None;
+ rtTimestamp = 0;
+ referenceTimeDuration = 0;
+ }
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (pBuffer != null)
+ {
+ ((MediaBuffer)pBuffer).Dispose();
+ pBuffer = null;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ ///
+ /// Media Buffer
+ ///
+ public IMediaBuffer MediaBuffer
+ {
+ get { return pBuffer; }
+ internal set { pBuffer = value; }
+ }
+
+ ///
+ /// Length of data in buffer
+ ///
+ public int Length
+ {
+ get { return ((MediaBuffer)pBuffer).Length; }
+ }
+
+ ///
+ /// Status Flags
+ ///
+ public DmoOutputDataBufferFlags StatusFlags
+ {
+ get { return dwStatus; }
+ internal set { dwStatus = value; }
+ }
+
+ ///
+ /// Timestamp
+ ///
+ public long Timestamp
+ {
+ get { return rtTimestamp; }
+ internal set { rtTimestamp = value; }
+ }
+
+ ///
+ /// Duration
+ ///
+ public long Duration
+ {
+ get { return referenceTimeDuration; }
+ internal set { referenceTimeDuration = value; }
+ }
+
+ ///
+ /// Retrives the data in this buffer
+ ///
+ /// Buffer to receive data
+ /// Offset into buffer
+ public void RetrieveData(byte[] data, int offset)
+ {
+ ((MediaBuffer)pBuffer).RetrieveData(data, offset);
+ }
+
+ ///
+ /// Is more data available
+ /// If true, ProcessOuput should be called again
+ ///
+ public bool MoreDataAvailable
+ {
+ get
+ {
+ return (StatusFlags & DmoOutputDataBufferFlags.Incomplete) == DmoOutputDataBufferFlags.Incomplete;
+ }
+ }
+ }
+}
diff --git a/NAudio/Dmo/DmoOutputDataBufferFlags.cs b/NAudio/Dmo/DmoOutputDataBufferFlags.cs
new file mode 100644
index 00000000..ee867fe6
--- /dev/null
+++ b/NAudio/Dmo/DmoOutputDataBufferFlags.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// DMO Output Data Buffer Flags
+ ///
+ [Flags]
+ public enum DmoOutputDataBufferFlags
+ {
+ ///
+ /// None
+ ///
+ None,
+ ///
+ /// DMO_OUTPUT_DATA_BUFFERF_SYNCPOINT
+ ///
+ SyncPoint = 0x00000001,
+ ///
+ /// DMO_OUTPUT_DATA_BUFFERF_TIME
+ ///
+ Time = 0x00000002,
+ ///
+ /// DMO_OUTPUT_DATA_BUFFERF_TIMELENGTH
+ ///
+ TimeLength = 0x00000004,
+ ///
+ /// DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE
+ ///
+ Incomplete = 0x01000000
+ }
+}
diff --git a/NAudio/Dmo/DmoPartialMediaType.cs b/NAudio/Dmo/DmoPartialMediaType.cs
new file mode 100644
index 00000000..050c96b2
--- /dev/null
+++ b/NAudio/Dmo/DmoPartialMediaType.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// DMO_PARTIAL_MEDIATYPE
+ ///
+ struct DmoPartialMediaType
+ {
+ Guid type;
+ Guid subtype;
+
+ public Guid Type
+ {
+ get { return type; }
+ internal set { type = value; }
+ }
+
+ public Guid Subtype
+ {
+ get { return subtype; }
+ internal set { subtype = value; }
+ }
+ }
+}
diff --git a/NAudio/Dmo/DmoProcessOutputFlags.cs b/NAudio/Dmo/DmoProcessOutputFlags.cs
new file mode 100644
index 00000000..40a17b76
--- /dev/null
+++ b/NAudio/Dmo/DmoProcessOutputFlags.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// DMO Process Output Flags
+ ///
+ [Flags]
+ public enum DmoProcessOutputFlags
+ {
+ ///
+ /// None
+ ///
+ None,
+ ///
+ /// DMO_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER
+ ///
+ DiscardWhenNoBuffer = 0x00000001
+ }
+}
diff --git a/NAudio/Dmo/DmoSetTypeFlags.cs b/NAudio/Dmo/DmoSetTypeFlags.cs
new file mode 100644
index 00000000..4004a305
--- /dev/null
+++ b/NAudio/Dmo/DmoSetTypeFlags.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ [Flags]
+ enum DmoSetTypeFlags
+ {
+ None,
+ DMO_SET_TYPEF_TEST_ONLY = 0x00000001,
+ DMO_SET_TYPEF_CLEAR = 0x00000002
+ }
+}
diff --git a/NAudio/Dmo/IEnumDmo.cs b/NAudio/Dmo/IEnumDmo.cs
new file mode 100644
index 00000000..1f72cab7
--- /dev/null
+++ b/NAudio/Dmo/IEnumDmo.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ [Guid("2c3cd98a-2bfa-4a53-9c27-5249ba64ba0f"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IEnumDmo
+ {
+ // int Next(int itemsToFetch, CLSID[] clsids, string[] names, out int itemsFetched);
+ // lets do one at a time to keep it simple - don't call with itemsToFetch > 1
+ int Next(int itemsToFetch, out Guid clsid, out IntPtr name, out int itemsFetched);
+
+ int Skip(int itemsToSkip);
+
+ int Reset();
+
+ int Clone(out IEnumDmo enumPointer);
+ }
+}
diff --git a/NAudio/Dmo/IMediaBuffer.cs b/NAudio/Dmo/IMediaBuffer.cs
new file mode 100644
index 00000000..c1a07779
--- /dev/null
+++ b/NAudio/Dmo/IMediaBuffer.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// IMediaBuffer Interface
+ ///
+ [ComImport,
+#if !NETFX_CORE
+ System.Security.SuppressUnmanagedCodeSecurity,
+#endif
+ Guid("59eff8b9-938c-4a26-82f2-95cb84cdc837"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IMediaBuffer
+ {
+ ///
+ /// Set Length
+ ///
+ /// Length
+ /// HRESULT
+ [PreserveSig]
+ int SetLength(int length);
+
+ ///
+ /// Get Max Length
+ ///
+ /// Max Length
+ /// HRESULT
+ [PreserveSig]
+ int GetMaxLength(out int maxLength);
+
+ ///
+ /// Get Buffer and Length
+ ///
+ /// Pointer to variable into which to write the Buffer Pointer
+ /// Pointer to variable into which to write the Valid Data Length
+ /// HRESULT
+ [PreserveSig]
+ int GetBufferAndLength(IntPtr bufferPointerPointer, IntPtr validDataLengthPointer);
+ }
+}
diff --git a/NAudio/Dmo/IMediaObject.cs b/NAudio/Dmo/IMediaObject.cs
new file mode 100644
index 00000000..f754a97e
--- /dev/null
+++ b/NAudio/Dmo/IMediaObject.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// defined in mediaobj.h
+ ///
+ [ComImport,
+#if !NETFX_CORE
+ System.Security.SuppressUnmanagedCodeSecurity,
+#endif
+ Guid("d8ad0f58-5494-4102-97c5-ec798e59bcf4"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IMediaObject
+ {
+ [PreserveSig]
+ int GetStreamCount(out int inputStreams, out int outputStreams);
+
+ [PreserveSig]
+ int GetInputStreamInfo(int inputStreamIndex, out InputStreamInfoFlags flags);
+
+ [PreserveSig]
+ int GetOutputStreamInfo(int outputStreamIndex, out OutputStreamInfoFlags flags);
+
+ [PreserveSig]
+ int GetInputType(int inputStreamIndex, int typeIndex, out DmoMediaType mediaType);
+
+ [PreserveSig]
+ int GetOutputType(int outputStreamIndex, int typeIndex, out DmoMediaType mediaType);
+
+ [PreserveSig]
+ int SetInputType(int inputStreamIndex, [In] ref DmoMediaType mediaType, DmoSetTypeFlags flags);
+
+ [PreserveSig]
+ int SetOutputType(int outputStreamIndex, [In] ref DmoMediaType mediaType, DmoSetTypeFlags flags);
+
+ [PreserveSig]
+ int GetInputCurrentType(int inputStreamIndex, out DmoMediaType mediaType);
+
+ [PreserveSig]
+ int GetOutputCurrentType(int outputStreamIndex, out DmoMediaType mediaType);
+
+ [PreserveSig]
+ int GetInputSizeInfo(int inputStreamIndex, out int size, out int maxLookahead, out int alignment);
+
+ [PreserveSig]
+ int GetOutputSizeInfo(int outputStreamIndex, out int size, out int alignment);
+
+ [PreserveSig]
+ int GetInputMaxLatency(int inputStreamIndex, out long referenceTimeMaxLatency);
+
+ [PreserveSig]
+ int SetInputMaxLatency(int inputStreamIndex, long referenceTimeMaxLatency);
+
+ [PreserveSig]
+ int Flush();
+
+ [PreserveSig]
+ int Discontinuity(int inputStreamIndex);
+
+ [PreserveSig]
+ int AllocateStreamingResources();
+
+ [PreserveSig]
+ int FreeStreamingResources();
+
+ [PreserveSig]
+ int GetInputStatus(int inputStreamIndex, out DmoInputStatusFlags flags);
+
+ [PreserveSig]
+ int ProcessInput(int inputStreamIndex, [In] IMediaBuffer mediaBuffer, DmoInputDataBufferFlags flags,
+ long referenceTimeTimestamp, long referenceTimeDuration);
+
+ [PreserveSig]
+ int ProcessOutput(DmoProcessOutputFlags flags,
+ int outputBufferCount,
+ [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] DmoOutputDataBuffer[] outputBuffers,
+ out int statusReserved);
+
+ [PreserveSig]
+ int Lock(bool acquireLock);
+ }
+}
diff --git a/NAudio/Dmo/IMediaParamInfo.cs b/NAudio/Dmo/IMediaParamInfo.cs
new file mode 100644
index 00000000..978e2009
--- /dev/null
+++ b/NAudio/Dmo/IMediaParamInfo.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// defined in Medparam.h
+ ///
+ [ComImport,
+#if !NETFX_CORE
+ System.Security.SuppressUnmanagedCodeSecurity,
+#endif
+ Guid("6d6cbb60-a223-44aa-842f-a2f06750be6d"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IMediaParamInfo
+ {
+ [PreserveSig]
+ int GetParamCount(out int paramCount);
+
+ [PreserveSig] // MP_PARAMINFO
+ int GetParamInfo(int paramIndex, ref MediaParamInfo paramInfo);
+
+ [PreserveSig]
+ int GetParamText(int paramIndex, out IntPtr paramText);
+
+ [PreserveSig]
+ int GetNumTimeFormats(out int numTimeFormats);
+
+ [PreserveSig]
+ int GetSupportedTimeFormat(int formatIndex, out Guid guidTimeFormat);
+
+ [PreserveSig] // MP_TIMEDATA is a DWORD
+ int GetCurrentTimeFormat(out Guid guidTimeFormat, out int mediaTimeData);
+ }
+}
diff --git a/NAudio/Dmo/IWMResamplerProps.cs b/NAudio/Dmo/IWMResamplerProps.cs
new file mode 100644
index 00000000..5bb7c8fa
--- /dev/null
+++ b/NAudio/Dmo/IWMResamplerProps.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// Windows Media Resampler Props
+ /// wmcodecdsp.h
+ ///
+ [Guid("E7E9984F-F09F-4da4-903F-6E2E0EFE56B5"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ interface IWMResamplerProps
+ {
+ ///
+ /// Range is 1 to 60
+ ///
+ int SetHalfFilterLength(int outputQuality);
+
+ ///
+ /// Specifies the channel matrix.
+ ///
+ int SetUserChannelMtx([In] float[] channelConversionMatrix);
+ }
+}
diff --git a/NAudio/Dmo/InputStreamInfoFlags.cs b/NAudio/Dmo/InputStreamInfoFlags.cs
new file mode 100644
index 00000000..f6f50460
--- /dev/null
+++ b/NAudio/Dmo/InputStreamInfoFlags.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ [Flags]
+ enum InputStreamInfoFlags
+ {
+ None,
+ DMO_INPUT_STREAMF_WHOLE_SAMPLES = 0x00000001,
+ DMO_INPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER = 0x00000002,
+ DMO_INPUT_STREAMF_FIXED_SAMPLE_SIZE = 0x00000004,
+ DMO_INPUT_STREAMF_HOLDS_BUFFERS = 0x00000008
+ }
+}
diff --git a/NAudio/Dmo/MediaBuffer.cs b/NAudio/Dmo/MediaBuffer.cs
new file mode 100644
index 00000000..d93245b5
--- /dev/null
+++ b/NAudio/Dmo/MediaBuffer.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using NAudio.Utils;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// Attempting to implement the COM IMediaBuffer interface as a .NET object
+ /// Not sure what will happen when I pass this to an unmanaged object
+ ///
+ public class MediaBuffer : IMediaBuffer, IDisposable
+ {
+ IntPtr buffer;
+ int length;
+ int maxLength;
+
+ ///
+ /// Creates a new Media Buffer
+ ///
+ /// Maximum length in bytes
+ public MediaBuffer(int maxLength)
+ {
+ this.buffer = Marshal.AllocCoTaskMem(maxLength);
+ this.maxLength = maxLength;
+ }
+
+ ///
+ /// Dispose and free memory for buffer
+ ///
+ public void Dispose()
+ {
+ if (buffer != IntPtr.Zero)
+ {
+ Marshal.FreeCoTaskMem(buffer);
+ buffer = IntPtr.Zero;
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ~MediaBuffer()
+ {
+ Dispose();
+ }
+
+ #region IMediaBuffer Members
+
+ ///
+ /// Set length of valid data in the buffer
+ ///
+ /// length
+ /// HRESULT
+ int IMediaBuffer.SetLength(int length)
+ {
+ //System.Diagnostics.Debug.WriteLine(String.Format("Set Length {0}", length));
+ if (length > maxLength)
+ {
+ return HResult.E_INVALIDARG;
+ }
+ this.length = length;
+ return HResult.S_OK;
+ }
+
+ ///
+ /// Gets the maximum length of the buffer
+ ///
+ /// Max length (output parameter)
+ /// HRESULT
+ int IMediaBuffer.GetMaxLength(out int maxLength)
+ {
+ //System.Diagnostics.Debug.WriteLine("Get Max Length");
+ maxLength = this.maxLength;
+ return HResult.S_OK;
+ }
+
+ ///
+ /// Gets buffer and / or length
+ ///
+ /// Pointer to variable into which buffer pointer should be written
+ /// Pointer to variable into which valid data length should be written
+ /// HRESULT
+ int IMediaBuffer.GetBufferAndLength(IntPtr bufferPointerPointer, IntPtr validDataLengthPointer)
+ {
+
+ //System.Diagnostics.Debug.WriteLine(String.Format("Get Buffer and Length {0},{1}",
+ // bufferPointerPointer,validDataLengthPointer));
+ if (bufferPointerPointer != IntPtr.Zero)
+ {
+ Marshal.WriteIntPtr(bufferPointerPointer, this.buffer);
+ }
+ if (validDataLengthPointer != IntPtr.Zero)
+ {
+ Marshal.WriteInt32(validDataLengthPointer, this.length);
+
+ }
+ //System.Diagnostics.Debug.WriteLine("Finished Getting Buffer and Length");
+ return HResult.S_OK;
+
+ }
+
+ #endregion
+
+ ///
+ /// Length of data in the media buffer
+ ///
+ public int Length
+ {
+ get { return length; }
+ set
+ {
+ if (length > maxLength)
+ {
+ throw new ArgumentException("Cannot be greater than maximum buffer size");
+ }
+ length = value;
+ }
+ }
+
+ ///
+ /// Loads data into this buffer
+ ///
+ /// Data to load
+ /// Number of bytes to load
+ public void LoadData(byte[] data, int bytes)
+ {
+ this.Length = bytes;
+ Marshal.Copy(data, 0, buffer, bytes);
+ }
+
+ ///
+ /// Retrieves the data in the output buffer
+ ///
+ /// buffer to retrieve into
+ /// offset within that buffer
+ public void RetrieveData(byte[] data, int offset)
+ {
+ Marshal.Copy(buffer, data, offset, Length);
+ }
+ }
+}
diff --git a/NAudio/Dmo/MediaObject.cs b/NAudio/Dmo/MediaObject.cs
new file mode 100644
index 00000000..c0e4059d
--- /dev/null
+++ b/NAudio/Dmo/MediaObject.cs
@@ -0,0 +1,492 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// Media Object
+ ///
+ public class MediaObject : IDisposable
+ {
+ IMediaObject mediaObject;
+ int inputStreams;
+ int outputStreams;
+
+ #region Construction
+
+ ///
+ /// Creates a new Media Object
+ ///
+ /// Media Object COM interface
+ internal MediaObject(IMediaObject mediaObject)
+ {
+ this.mediaObject = mediaObject;
+ mediaObject.GetStreamCount(out inputStreams, out outputStreams);
+ }
+
+ #endregion
+
+ #region Public Properties
+ ///
+ /// Number of input streams
+ ///
+ public int InputStreamCount
+ {
+ get { return inputStreams; }
+ }
+
+ ///
+ /// Number of output streams
+ ///
+ public int OutputStreamCount
+ {
+ get { return outputStreams; }
+ }
+ #endregion
+
+ #region Get Input and Output Types
+
+ ///
+ /// Gets the input media type for the specified input stream
+ ///
+ /// Input stream index
+ /// Input type index
+ /// DMO Media Type or null if there are no more input types
+ public DmoMediaType? GetInputType(int inputStream, int inputTypeIndex)
+ {
+ try
+ {
+ DmoMediaType mediaType;
+ int hresult = mediaObject.GetInputType(inputStream, inputTypeIndex, out mediaType);
+ if (hresult == HResult.S_OK)
+ {
+ // this frees the format (if present)
+ // we should therefore come up with a way of marshaling the format
+ // into a completely managed structure
+ DmoInterop.MoFreeMediaType(ref mediaType);
+ return mediaType;
+ }
+ }
+ catch (COMException e)
+ {
+ if (e.GetHResult() != (int)DmoHResults.DMO_E_NO_MORE_ITEMS)
+ {
+ throw;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Gets the DMO Media Output type
+ ///
+ /// The output stream
+ /// Output type index
+ /// DMO Media Type or null if no more available
+ public DmoMediaType? GetOutputType(int outputStream, int outputTypeIndex)
+ {
+ try
+ {
+ DmoMediaType mediaType;
+ int hresult = mediaObject.GetOutputType(outputStream, outputTypeIndex, out mediaType);
+ if (hresult == HResult.S_OK)
+ {
+ // this frees the format (if present)
+ // we should therefore come up with a way of marshaling the format
+ // into a completely managed structure
+ DmoInterop.MoFreeMediaType(ref mediaType);
+ return mediaType;
+ }
+ }
+ catch (COMException e)
+ {
+ if (e.GetHResult() != (int)DmoHResults.DMO_E_NO_MORE_ITEMS)
+ {
+ throw;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// retrieves the media type that was set for an output stream, if any
+ ///
+ /// Output stream index
+ /// DMO Media Type or null if no more available
+ public DmoMediaType GetOutputCurrentType(int outputStreamIndex)
+ {
+ DmoMediaType mediaType;
+ int hresult = mediaObject.GetOutputCurrentType(outputStreamIndex, out mediaType);
+ if (hresult == HResult.S_OK)
+ {
+ // this frees the format (if present)
+ // we should therefore come up with a way of marshaling the format
+ // into a completely managed structure
+ DmoInterop.MoFreeMediaType(ref mediaType);
+ return mediaType;
+ }
+ else
+ {
+ if (hresult == (int)DmoHResults.DMO_E_TYPE_NOT_SET)
+ {
+ throw new InvalidOperationException("Media type was not set.");
+ }
+ else
+ {
+ throw Marshal.GetExceptionForHR(hresult);
+ }
+ }
+ }
+
+ ///
+ /// Enumerates the supported input types
+ ///
+ /// Input stream index
+ /// Enumeration of input types
+ public IEnumerable GetInputTypes(int inputStreamIndex)
+ {
+ int typeIndex = 0;
+ DmoMediaType? mediaType;
+ while ((mediaType = GetInputType(inputStreamIndex,typeIndex)) != null)
+ {
+ yield return mediaType.Value;
+ typeIndex++;
+ }
+ }
+
+ ///
+ /// Enumerates the output types
+ ///
+ /// Output stream index
+ /// Enumeration of supported output types
+ public IEnumerable GetOutputTypes(int outputStreamIndex)
+ {
+ int typeIndex = 0;
+ DmoMediaType? mediaType;
+ while ((mediaType = GetOutputType(outputStreamIndex, typeIndex)) != null)
+ {
+ yield return mediaType.Value;
+ typeIndex++;
+ }
+ }
+
+ #endregion
+
+ #region Set Input Type
+
+ ///
+ /// Querys whether a specified input type is supported
+ ///
+ /// Input stream index
+ /// Media type to check
+ /// true if supports
+ public bool SupportsInputType(int inputStreamIndex, DmoMediaType mediaType)
+ {
+ return SetInputType(inputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
+ }
+
+ ///
+ /// Sets the input type helper method
+ ///
+ /// Input stream index
+ /// Media type
+ /// Flags (can be used to test rather than set)
+ private bool SetInputType(int inputStreamIndex, DmoMediaType mediaType, DmoSetTypeFlags flags)
+ {
+ int hResult = mediaObject.SetInputType(inputStreamIndex, ref mediaType, flags);
+ if (hResult != HResult.S_OK)
+ {
+ if (hResult == (int)DmoHResults.DMO_E_INVALIDSTREAMINDEX)
+ {
+ throw new ArgumentException("Invalid stream index");
+ }
+ if (hResult == (int)DmoHResults.DMO_E_TYPE_NOT_ACCEPTED)
+ {
+ Debug.WriteLine("Media type was not accepted");
+ }
+
+ return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Sets the input type
+ ///
+ /// Input stream index
+ /// Media Type
+ public void SetInputType(int inputStreamIndex, DmoMediaType mediaType)
+ {
+ if(!SetInputType(inputStreamIndex,mediaType,DmoSetTypeFlags.None))
+ {
+ throw new ArgumentException("Media Type not supported");
+ }
+ }
+
+ ///
+ /// Sets the input type to the specified Wave format
+ ///
+ /// Input stream index
+ /// Wave format
+ public void SetInputWaveFormat(int inputStreamIndex, WaveFormat waveFormat)
+ {
+ DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
+ bool set = SetInputType(inputStreamIndex, mediaType, DmoSetTypeFlags.None);
+ DmoInterop.MoFreeMediaType(ref mediaType);
+ if (!set)
+ {
+ throw new ArgumentException("Media Type not supported");
+ }
+ }
+
+ ///
+ /// Requests whether the specified Wave format is supported as an input
+ ///
+ /// Input stream index
+ /// Wave format
+ /// true if supported
+ public bool SupportsInputWaveFormat(int inputStreamIndex, WaveFormat waveFormat)
+ {
+ DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
+ bool supported = SetInputType(inputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
+ DmoInterop.MoFreeMediaType(ref mediaType);
+ return supported;
+ }
+
+ ///
+ /// Helper function to make a DMO Media Type to represent a particular WaveFormat
+ ///
+ private DmoMediaType CreateDmoMediaTypeForWaveFormat(WaveFormat waveFormat)
+ {
+ DmoMediaType mediaType = new DmoMediaType();
+ int waveFormatExSize = Marshal.SizeOf(waveFormat); // 18 + waveFormat.ExtraSize;
+ DmoInterop.MoInitMediaType(ref mediaType, waveFormatExSize);
+ mediaType.SetWaveFormat(waveFormat);
+ return mediaType;
+ }
+
+ #endregion
+
+ #region Set Output Type
+
+ ///
+ /// Checks if a specified output type is supported
+ /// n.b. you may need to set the input type first
+ ///
+ /// Output stream index
+ /// Media type
+ /// True if supported
+ public bool SupportsOutputType(int outputStreamIndex, DmoMediaType mediaType)
+ {
+ return SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
+ }
+
+ ///
+ /// Tests if the specified Wave Format is supported for output
+ /// n.b. may need to set the input type first
+ ///
+ /// Output stream index
+ /// Wave format
+ /// True if supported
+ public bool SupportsOutputWaveFormat(int outputStreamIndex, WaveFormat waveFormat)
+ {
+ DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
+ bool supported = SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
+ DmoInterop.MoFreeMediaType(ref mediaType);
+ return supported;
+ }
+
+ ///
+ /// Helper method to call SetOutputType
+ ///
+ private bool SetOutputType(int outputStreamIndex, DmoMediaType mediaType, DmoSetTypeFlags flags)
+ {
+ int hresult = mediaObject.SetOutputType(outputStreamIndex, ref mediaType, flags);
+ if (hresult == (int)DmoHResults.DMO_E_TYPE_NOT_ACCEPTED)
+ {
+ return false;
+ }
+ else if (hresult == HResult.S_OK)
+ {
+ return true;
+ }
+ else
+ {
+ throw Marshal.GetExceptionForHR(hresult);
+ }
+ }
+
+ ///
+ /// Sets the output type
+ /// n.b. may need to set the input type first
+ ///
+ /// Output stream index
+ /// Media type to set
+ public void SetOutputType(int outputStreamIndex, DmoMediaType mediaType)
+ {
+ if (!SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.None))
+ {
+ throw new ArgumentException("Media Type not supported");
+ }
+ }
+
+ ///
+ /// Set output type to the specified wave format
+ /// n.b. may need to set input type first
+ ///
+ /// Output stream index
+ /// Wave format
+ public void SetOutputWaveFormat(int outputStreamIndex, WaveFormat waveFormat)
+ {
+ DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
+ bool succeeded = SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.None);
+ DmoInterop.MoFreeMediaType(ref mediaType);
+ if (!succeeded)
+ {
+ throw new ArgumentException("Media Type not supported");
+ }
+ }
+
+ #endregion
+
+ #region Get Input and Output Size Info
+ ///
+ /// Get Input Size Info
+ ///
+ /// Input Stream Index
+ /// Input Size Info
+ public MediaObjectSizeInfo GetInputSizeInfo(int inputStreamIndex)
+ {
+ int size;
+ int maxLookahead;
+ int alignment;
+ Marshal.ThrowExceptionForHR(mediaObject.GetInputSizeInfo(inputStreamIndex, out size, out maxLookahead, out alignment));
+ return new MediaObjectSizeInfo(size, maxLookahead, alignment);
+ }
+
+ ///
+ /// Get Output Size Info
+ ///
+ /// Output Stream Index
+ /// Output Size Info
+ public MediaObjectSizeInfo GetOutputSizeInfo(int outputStreamIndex)
+ {
+ int size;
+ int alignment;
+ Marshal.ThrowExceptionForHR(mediaObject.GetOutputSizeInfo(outputStreamIndex, out size, out alignment));
+ return new MediaObjectSizeInfo(size, 0, alignment);
+ }
+
+ #endregion
+
+ #region Buffer Processing
+ ///
+ /// Process Input
+ ///
+ /// Input Stream index
+ /// Media Buffer
+ /// Flags
+ /// Timestamp
+ /// Duration
+ public void ProcessInput(int inputStreamIndex, IMediaBuffer mediaBuffer, DmoInputDataBufferFlags flags,
+ long timestamp, long duration)
+ {
+ Marshal.ThrowExceptionForHR(mediaObject.ProcessInput(inputStreamIndex, mediaBuffer, flags, timestamp, duration));
+ }
+
+ ///
+ /// Process Output
+ ///
+ /// Flags
+ /// Output buffer count
+ /// Output buffers
+ public void ProcessOutput(DmoProcessOutputFlags flags, int outputBufferCount, DmoOutputDataBuffer[] outputBuffers)
+ {
+ int reserved;
+ Marshal.ThrowExceptionForHR(mediaObject.ProcessOutput(flags, outputBufferCount, outputBuffers, out reserved));
+ }
+ #endregion
+
+ ///
+ /// Gives the DMO a chance to allocate any resources needed for streaming
+ ///
+ public void AllocateStreamingResources()
+ {
+ Marshal.ThrowExceptionForHR(mediaObject.AllocateStreamingResources());
+ }
+
+ ///
+ /// Tells the DMO to free any resources needed for streaming
+ ///
+ public void FreeStreamingResources()
+ {
+ Marshal.ThrowExceptionForHR(mediaObject.FreeStreamingResources());
+ }
+
+ ///
+ /// Gets maximum input latency
+ ///
+ /// input stream index
+ /// Maximum input latency as a ref-time
+ public long GetInputMaxLatency(int inputStreamIndex)
+ {
+ long maxLatency;
+ Marshal.ThrowExceptionForHR(mediaObject.GetInputMaxLatency(inputStreamIndex, out maxLatency));
+ return maxLatency;
+ }
+
+ ///
+ /// Flushes all buffered data
+ ///
+ public void Flush()
+ {
+ Marshal.ThrowExceptionForHR(mediaObject.Flush());
+ }
+
+ ///
+ /// Report a discontinuity on the specified input stream
+ ///
+ /// Input Stream index
+ public void Discontinuity(int inputStreamIndex)
+ {
+ Marshal.ThrowExceptionForHR(mediaObject.Discontinuity(inputStreamIndex));
+ }
+
+ ///
+ /// Is this input stream accepting data?
+ ///
+ /// Input Stream index
+ /// true if accepting data
+ public bool IsAcceptingData(int inputStreamIndex)
+ {
+ DmoInputStatusFlags flags;
+ int hresult = mediaObject.GetInputStatus(inputStreamIndex, out flags);
+ Marshal.ThrowExceptionForHR(hresult);
+ return (flags & DmoInputStatusFlags.DMO_INPUT_STATUSF_ACCEPT_DATA) == DmoInputStatusFlags.DMO_INPUT_STATUSF_ACCEPT_DATA;
+ }
+
+ // TODO: there are still several IMediaObject functions to be wrapped
+
+ #region IDisposable Members
+
+ ///
+ /// Experimental code, not currently being called
+ /// Not sure if it is necessary anyway
+ ///
+ public void Dispose()
+ {
+ if (mediaObject != null)
+ {
+ Marshal.ReleaseComObject(mediaObject);
+ mediaObject = null;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Dmo/MediaObjectSizeInfo.cs b/NAudio/Dmo/MediaObjectSizeInfo.cs
new file mode 100644
index 00000000..b698ea88
--- /dev/null
+++ b/NAudio/Dmo/MediaObjectSizeInfo.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// Media Object Size Info
+ ///
+ public class MediaObjectSizeInfo
+ {
+ ///
+ /// Minimum Buffer Size, in bytes
+ ///
+ public int Size { get; private set; }
+
+ ///
+ /// Max Lookahead
+ ///
+ public int MaxLookahead { get; private set; }
+
+ ///
+ /// Alignment
+ ///
+ public int Alignment { get; private set; }
+
+ ///
+ /// Media Object Size Info
+ ///
+ public MediaObjectSizeInfo(int size, int maxLookahead, int alignment)
+ {
+ Size = size;
+ MaxLookahead = maxLookahead;
+ Alignment = alignment;
+ }
+
+ ///
+ /// ToString
+ ///
+ public override string ToString()
+ {
+ return String.Format("Size: {0}, Alignment {1}, MaxLookahead {2}",
+ Size, Alignment, MaxLookahead);
+ }
+
+ }
+}
diff --git a/NAudio/Dmo/MediaParamInfo.cs b/NAudio/Dmo/MediaParamInfo.cs
new file mode 100644
index 00000000..897ee7eb
--- /dev/null
+++ b/NAudio/Dmo/MediaParamInfo.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// MP_PARAMINFO
+ ///
+ struct MediaParamInfo
+ {
+#pragma warning disable 0649
+ public MediaParamType mpType;
+ public MediaParamCurveType mopCaps;
+ public float mpdMinValue; // MP_DATA is a float
+ public float mpdMaxValue;
+ public float mpdNeutralValue;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string szUnitText;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string szLabel;
+#pragma warning restore 0649
+ }
+
+ ///
+ /// MP_TYPE
+ ///
+ enum MediaParamType
+ {
+ ///
+ /// MPT_INT
+ ///
+ Int,
+ ///
+ /// MPT_FLOAT
+ ///
+ Float,
+ ///
+ /// MPT_BOOL
+ ///
+ Bool,
+ ///
+ /// MPT_ENUM
+ ///
+ Enum,
+ ///
+ /// MPT_MAX
+ ///
+ Max,
+ }
+
+ ///
+ /// MP_CURVE_TYPE
+ ///
+ [Flags]
+ internal enum MediaParamCurveType
+ {
+ MP_CURVE_JUMP = 0x1,
+ MP_CURVE_LINEAR = 0x2,
+ MP_CURVE_SQUARE = 0x4,
+ MP_CURVE_INVSQUARE = 0x8,
+ MP_CURVE_SINE = 0x10
+ }
+
+}
diff --git a/NAudio/Dmo/MediaTypes.cs b/NAudio/Dmo/MediaTypes.cs
new file mode 100644
index 00000000..70f9bb7d
--- /dev/null
+++ b/NAudio/Dmo/MediaTypes.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// uuids.h, ksuuids.h
+ ///
+ static class MediaTypes
+ {
+ public static readonly Guid MEDIATYPE_AnalogAudio = new Guid("0482DEE1-7817-11cf-8a03-00aa006ecb65"); // Analog audio.
+ public static readonly Guid MEDIATYPE_AnalogVideo = new Guid("0482DDE1-7817-11cf-8A03-00AA006ECB65"); // Analog video.
+ public static readonly Guid MEDIATYPE_Audio = new Guid("73647561-0000-0010-8000-00AA00389B71"); // Audio.
+ public static readonly Guid MEDIATYPE_AUXLine21Data = new Guid("670AEA80-3A82-11d0-B79B-00AA003767A7"); // Line 21 data. Used by closed captions.
+ public static readonly Guid MEDIATYPE_File = new Guid("656c6966-0000-0010-8000-00AA00389B71"); // File. (Obsolete)
+ public static readonly Guid MEDIATYPE_Interleaved = new Guid("73766169-0000-0010-8000-00AA00389B71"); // Interleaved audio and video. Used for Digital Video (DV).
+ public static readonly Guid MEDIATYPE_Midi = new Guid("7364696D-0000-0010-8000-00AA00389B71"); // MIDI format.
+ // public static readonly Guid MEDIATYPE_MPEG2_PES MPEG-2 PES packets.
+ // public static readonly Guid MEDIATYPE_MPEG2_SECTIONS MPEG-2 section data
+ public static readonly Guid MEDIATYPE_ScriptCommand = new Guid("73636d64-0000-0010-8000-00AA00389B71"); // Data is a script command, used by closed captions.
+ public static readonly Guid MEDIATYPE_Stream = new Guid("e436eb83-524f-11ce-9f53-0020af0ba770"); // Byte stream with no time stamps.
+ public static readonly Guid MEDIATYPE_Text = new Guid("73747874-0000-0010-8000-00AA00389B71"); // Text.
+ public static readonly Guid MEDIATYPE_Timecode = new Guid("0482DEE3-7817-11cf-8a03-00aa006ecb65"); // Timecode data. Note: DirectShow does not provide any filters that support this media type.
+ public static readonly Guid MEDIATYPE_Video = new Guid("73646976-0000-0010-8000-00AA00389B71"); // Video.
+
+ public static readonly Guid[] MajorTypes = new Guid[]
+ {
+ MEDIATYPE_AnalogAudio,
+ MEDIATYPE_AnalogVideo,
+ MEDIATYPE_Audio,
+ MEDIATYPE_AUXLine21Data,
+ MEDIATYPE_File,
+ MEDIATYPE_Interleaved,
+ MEDIATYPE_Midi,
+ MEDIATYPE_ScriptCommand,
+ MEDIATYPE_Stream,
+ MEDIATYPE_Text,
+ MEDIATYPE_Timecode,
+ MEDIATYPE_Video,
+ };
+
+ public static readonly string[] MajorTypeNames = new string[]
+ {
+ "Analog Audio",
+ "Analog Video",
+ "Audio",
+ "AUXLine21Data",
+ "File",
+ "Interleaved",
+ "Midi",
+ "ScriptCommand",
+ "Stream",
+ "Text",
+ "Timecode",
+ "Video",
+ };
+
+
+ public static string GetMediaTypeName(Guid majorType)
+ {
+ for (int index = 0; index < MajorTypes.Length; index++)
+ {
+ if (majorType == MajorTypes[index])
+ {
+ return MajorTypeNames[index];
+ }
+ }
+ throw new ArgumentException("Major Type not found");
+ }
+ }
+
+
+}
diff --git a/NAudio/Dmo/OutputStreamInfoFlags.cs b/NAudio/Dmo/OutputStreamInfoFlags.cs
new file mode 100644
index 00000000..2e0440bc
--- /dev/null
+++ b/NAudio/Dmo/OutputStreamInfoFlags.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dmo
+{
+ [Flags]
+ enum OutputStreamInfoFlags
+ {
+ DMO_OUTPUT_STREAMF_WHOLE_SAMPLES = 0x00000001,
+ DMO_OUTPUT_STREAMF_SINGLE_SAMPLE_PER_BUFFER = 0x00000002,
+ DMO_OUTPUT_STREAMF_FIXED_SAMPLE_SIZE = 0x00000004,
+ DMO_OUTPUT_STREAMF_DISCARDABLE = 0x00000008,
+ DMO_OUTPUT_STREAMF_OPTIONAL = 0x00000010
+ }
+}
diff --git a/NAudio/Dmo/ResamplerMediaObject.cs b/NAudio/Dmo/ResamplerMediaObject.cs
new file mode 100644
index 00000000..ad7186a2
--- /dev/null
+++ b/NAudio/Dmo/ResamplerMediaObject.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.Dmo
+{
+ ///
+ /// From wmcodecsdp.h
+ /// Implements:
+ /// - IMediaObject
+ /// - IMFTransform (Media foundation - we will leave this for now as there is loads of MF stuff)
+ /// - IPropertyStore
+ /// - IWMResamplerProps
+ /// Can resample PCM or IEEE
+ ///
+ [ComImport, Guid("f447b69e-1884-4a7e-8055-346f74d6edb3")]
+ class ResamplerMediaComObject
+ {
+ }
+
+ ///
+ /// DMO Resampler
+ ///
+ public class DmoResampler : IDisposable
+ {
+ MediaObject mediaObject;
+ IPropertyStore propertyStoreInterface;
+ IWMResamplerProps resamplerPropsInterface;
+ ResamplerMediaComObject mediaComObject;
+
+ ///
+ /// Creates a new Resampler based on the DMO Resampler
+ ///
+ public DmoResampler()
+ {
+ mediaComObject = new ResamplerMediaComObject();
+ mediaObject = new MediaObject((IMediaObject)mediaComObject);
+ propertyStoreInterface = (IPropertyStore)mediaComObject;
+ resamplerPropsInterface = (IWMResamplerProps)mediaComObject;
+ }
+
+ ///
+ /// Media Object
+ ///
+ public MediaObject MediaObject
+ {
+ get
+ {
+ return mediaObject;
+ }
+ }
+
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose code - experimental at the moment
+ /// Was added trying to track down why Resampler crashes NUnit
+ /// This code not currently being called by ResamplerDmoStream
+ ///
+ public void Dispose()
+ {
+ if(propertyStoreInterface != null)
+ {
+ Marshal.ReleaseComObject(propertyStoreInterface);
+ propertyStoreInterface = null;
+ }
+ if(resamplerPropsInterface != null)
+ {
+ Marshal.ReleaseComObject(resamplerPropsInterface);
+ resamplerPropsInterface = null;
+ }
+ if (mediaObject != null)
+ {
+ mediaObject.Dispose();
+ mediaObject = null;
+ }
+ if (mediaComObject != null)
+ {
+ Marshal.ReleaseComObject(mediaComObject);
+ mediaComObject = null;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Dmo/WindowsMediaMp3Decoder.cs b/NAudio/Dmo/WindowsMediaMp3Decoder.cs
new file mode 100644
index 00000000..58d9dbc7
--- /dev/null
+++ b/NAudio/Dmo/WindowsMediaMp3Decoder.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.Dmo
+{
+ // http://msdn.microsoft.com/en-us/library/ff819509%28VS.85%29.aspx
+ // CLSID_CMP3DecMediaObject
+
+ ///
+ /// implements IMediaObject (DirectX Media Object)
+ /// implements IMFTransform (Media Foundation Transform)
+ /// On Windows XP, it is always an MM (if present at all)
+ ///
+ [ComImport, Guid("bbeea841-0a63-4f52-a7ab-a9b3a84ed38a")]
+ class WindowsMediaMp3DecoderComObject
+ {
+ }
+
+ ///
+ /// Windows Media MP3 Decoder (as a DMO)
+ /// WORK IN PROGRESS - DO NOT USE!
+ ///
+ public class WindowsMediaMp3Decoder : IDisposable
+ {
+ MediaObject mediaObject;
+ IPropertyStore propertyStoreInterface;
+ //IWMResamplerProps resamplerPropsInterface;
+ WindowsMediaMp3DecoderComObject mediaComObject;
+
+ ///
+ /// Creates a new Resampler based on the DMO Resampler
+ ///
+ public WindowsMediaMp3Decoder()
+ {
+ mediaComObject = new WindowsMediaMp3DecoderComObject();
+ mediaObject = new MediaObject((IMediaObject)mediaComObject);
+ propertyStoreInterface = (IPropertyStore)mediaComObject;
+ //resamplerPropsInterface = (IWMResamplerProps)mediaComObject;
+ }
+
+ ///
+ /// Media Object
+ ///
+ public MediaObject MediaObject
+ {
+ get
+ {
+ return mediaObject;
+ }
+ }
+
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose code - experimental at the moment
+ /// Was added trying to track down why Resampler crashes NUnit
+ /// This code not currently being called by ResamplerDmoStream
+ ///
+ public void Dispose()
+ {
+ if(propertyStoreInterface != null)
+ {
+ Marshal.ReleaseComObject(propertyStoreInterface);
+ propertyStoreInterface = null;
+ }
+ /*if(resamplerPropsInterface != null)
+ {
+ Marshal.ReleaseComObject(resamplerPropsInterface);
+ resamplerPropsInterface = null;
+ }*/
+ if (mediaObject != null)
+ {
+ mediaObject.Dispose();
+ mediaObject = null;
+ }
+ if (mediaComObject != null)
+ {
+ Marshal.ReleaseComObject(mediaComObject);
+ mediaComObject = null;
+ }
+ }
+
+ #endregion
+ }
+
+}
diff --git a/NAudio/Dsp/BiQuadFilter.cs b/NAudio/Dsp/BiQuadFilter.cs
new file mode 100644
index 00000000..3f5a3cce
--- /dev/null
+++ b/NAudio/Dsp/BiQuadFilter.cs
@@ -0,0 +1,322 @@
+// based on Cookbook formulae for audio EQ biquad filter coefficients
+// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+// by Robert Bristow-Johnson
+
+// alpha = sin(w0)/(2*Q) (case: Q)
+// = sin(w0)*sinh( ln(2)/2 * BW * w0/sin(w0) ) (case: BW)
+// = sin(w0)/2 * sqrt( (A + 1/A)*(1/S - 1) + 2 ) (case: S)
+// Q: (the EE kind of definition, except for peakingEQ in which A*Q is
+// the classic EE Q. That adjustment in definition was made so that
+// a boost of N dB followed by a cut of N dB for identical Q and
+// f0/Fs results in a precisely flat unity gain filter or "wire".)
+//
+// BW: the bandwidth in octaves (between -3 dB frequencies for BPF
+// and notch or between midpoint (dBgain/2) gain frequencies for
+// peaking EQ)
+//
+// S: a "shelf slope" parameter (for shelving EQ only). When S = 1,
+// the shelf slope is as steep as it can be and remain monotonically
+// increasing or decreasing gain with frequency. The shelf slope, in
+// dB/octave, remains proportional to S for all other values for a
+// fixed f0/Fs and dBgain.
+
+using System;
+
+namespace NAudio.Dsp
+{
+ ///
+ /// BiQuad filter
+ ///
+ public class BiQuadFilter
+ {
+ // coefficients
+ private double a0;
+ private double a1;
+ private double a2;
+ private double a3;
+ private double a4;
+
+ // state
+ private float x1;
+ private float x2;
+ private float y1;
+ private float y2;
+
+ ///
+ /// Passes a single sample through the filter
+ ///
+ /// Input sample
+ /// Output sample
+ public float Transform(float inSample)
+ {
+ // compute result
+ var result = a0 * inSample + a1 * x1 + a2 * x2 - a3 * y1 - a4 * y2;
+
+ // shift x1 to x2, sample to x1
+ x2 = x1;
+ x1 = inSample;
+
+ // shift y1 to y2, result to y1
+ y2 = y1;
+ y1 = (float)result;
+
+ return y1;
+ }
+
+ private void SetCoefficients(double aa0, double aa1, double aa2, double b0, double b1, double b2)
+ {
+ // precompute the coefficients
+ a0 = b0/aa0;
+ a1 = b1/aa0;
+ a2 = b2/aa0;
+ a3 = aa1/aa0;
+ a4 = aa2/aa0;
+ }
+
+ ///
+ /// Set this up as a low pass filter
+ ///
+ /// Sample Rate
+ /// Cut-off Frequency
+ /// Bandwidth
+ public void SetLowPassFilter(float sampleRate, float cutoffFrequency, float q)
+ {
+ // H(s) = 1 / (s^2 + s/Q + 1)
+ var w0 = 2 * Math.PI * cutoffFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var alpha = Math.Sin(w0) / (2 * q);
+
+ var b0 = (1 - cosw0) / 2;
+ var b1 = 1 - cosw0;
+ var b2 = (1 - cosw0) / 2;
+ var aa0 = 1 + alpha;
+ var aa1 = -2 * cosw0;
+ var aa2 = 1 - alpha;
+ SetCoefficients(aa0,aa1,aa2,b0,b1,b2);
+ }
+
+ ///
+ /// Set this up as a peaking EQ
+ ///
+ /// Sample Rate
+ /// Centre Frequency
+ /// Bandwidth (Q)
+ /// Gain in decibels
+ public void SetPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain)
+ {
+ // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
+ var w0 = 2 * Math.PI * centreFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var sinw0 = Math.Sin(w0);
+ var alpha = sinw0 / (2 * q);
+ var a = Math.Pow(10, dbGain / 40); // TODO: should we square root this value?
+
+ var b0 = 1 + alpha * a;
+ var b1 = -2 * cosw0;
+ var b2 = 1 - alpha * a;
+ var aa0 = 1 + alpha / a;
+ var aa1 = -2 * cosw0;
+ var aa2 = 1 - alpha / a;
+ SetCoefficients(aa0, aa1, aa2, b0, b1, b2);
+ }
+
+ ///
+ /// Set this as a high pass filter
+ ///
+ public void SetHighPassFilter(float sampleRate, float cutoffFrequency, float q)
+ {
+ // H(s) = s^2 / (s^2 + s/Q + 1)
+ var w0 = 2 * Math.PI * cutoffFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var alpha = Math.Sin(w0) / (2 * q);
+
+ var b0 = (1 + cosw0) / 2;
+ var b1 = -(1 + cosw0);
+ var b2 = (1 + cosw0) / 2;
+ var aa0 = 1 + alpha;
+ var aa1 = -2 * cosw0;
+ var aa2 = 1 - alpha;
+ SetCoefficients(aa0, aa1, aa2, b0, b1, b2);
+ }
+
+ ///
+ /// Create a low pass filter
+ ///
+ public static BiQuadFilter LowPassFilter(float sampleRate, float cutoffFrequency, float q)
+ {
+ var filter = new BiQuadFilter();
+ filter.SetLowPassFilter(sampleRate,cutoffFrequency,q);
+ return filter;
+ }
+
+ ///
+ /// Create a High pass filter
+ ///
+ public static BiQuadFilter HighPassFilter(float sampleRate, float cutoffFrequency, float q)
+ {
+ var filter = new BiQuadFilter();
+ filter.SetHighPassFilter(sampleRate, cutoffFrequency, q);
+ return filter;
+ }
+
+ ///
+ /// Create a bandpass filter with constant skirt gain
+ ///
+ public static BiQuadFilter BandPassFilterConstantSkirtGain(float sampleRate, float centreFrequency, float q)
+ {
+ // H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
+ var w0 = 2 * Math.PI * centreFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var sinw0 = Math.Sin(w0);
+ var alpha = sinw0 / (2 * q);
+
+ var b0 = sinw0 / 2; // = Q*alpha
+ var b1 = 0;
+ var b2 = -sinw0 / 2; // = -Q*alpha
+ var a0 = 1 + alpha;
+ var a1 = -2 * cosw0;
+ var a2 = 1 - alpha;
+ return new BiQuadFilter(a0, a1, a2, b0, b1, b2);
+ }
+
+ ///
+ /// Create a bandpass filter with constant peak gain
+ ///
+ public static BiQuadFilter BandPassFilterConstantPeakGain(float sampleRate, float centreFrequency, float q)
+ {
+ // H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
+ var w0 = 2 * Math.PI * centreFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var sinw0 = Math.Sin(w0);
+ var alpha = sinw0 / (2 * q);
+
+ var b0 = alpha;
+ var b1 = 0;
+ var b2 = -alpha;
+ var a0 = 1 + alpha;
+ var a1 = -2 * cosw0;
+ var a2 = 1 - alpha;
+ return new BiQuadFilter(a0, a1, a2, b0, b1, b2);
+ }
+
+ ///
+ /// Creates a notch filter
+ ///
+ public static BiQuadFilter NotchFilter(float sampleRate, float centreFrequency, float q)
+ {
+ // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
+ var w0 = 2 * Math.PI * centreFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var sinw0 = Math.Sin(w0);
+ var alpha = sinw0 / (2 * q);
+
+ var b0 = 1;
+ var b1 = -2 * cosw0;
+ var b2 = 1;
+ var a0 = 1 + alpha;
+ var a1 = -2 * cosw0;
+ var a2 = 1 - alpha;
+ return new BiQuadFilter(a0, a1, a2, b0, b1, b2);
+ }
+
+ ///
+ /// Creaes an all pass filter
+ ///
+ public static BiQuadFilter AllPassFilter(float sampleRate, float centreFrequency, float q)
+ {
+ //H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
+ var w0 = 2 * Math.PI * centreFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var sinw0 = Math.Sin(w0);
+ var alpha = sinw0 / (2 * q);
+
+ var b0 = 1 - alpha;
+ var b1 = -2 * cosw0;
+ var b2 = 1 + alpha;
+ var a0 = 1 + alpha;
+ var a1 = -2 * cosw0;
+ var a2 = 1 - alpha;
+ return new BiQuadFilter(a0, a1, a2, b0, b1, b2);
+ }
+
+ ///
+ /// Create a Peaking EQ
+ ///
+ public static BiQuadFilter PeakingEQ(float sampleRate, float centreFrequency, float q, float dbGain)
+ {
+ var filter = new BiQuadFilter();
+ filter.SetPeakingEq(sampleRate, centreFrequency, q, dbGain);
+ return filter;
+ }
+
+ ///
+ /// H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
+ ///
+ ///
+ ///
+ /// a "shelf slope" parameter (for shelving EQ only).
+ /// When S = 1, the shelf slope is as steep as it can be and remain monotonically
+ /// increasing or decreasing gain with frequency. The shelf slope, in dB/octave,
+ /// remains proportional to S for all other values for a fixed f0/Fs and dBgain.
+ /// Gain in decibels
+ public static BiQuadFilter LowShelf(float sampleRate, float cutoffFrequency, float shelfSlope, float dbGain)
+ {
+ var w0 = 2 * Math.PI * cutoffFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var sinw0 = Math.Sin(w0);
+ var a = Math.Pow(10, dbGain / 40); // TODO: should we square root this value?
+ var alpha = sinw0 / 2 * Math.Sqrt((a + 1 / a) * (1 / shelfSlope - 1) + 2);
+ var temp = 2 * Math.Sqrt(a) * alpha;
+
+ var b0 = a * ((a + 1) - (a - 1) * cosw0 + temp);
+ var b1 = 2 * a * ((a - 1) - (a + 1) * cosw0);
+ var b2 = a * ((a + 1) - (a - 1) * cosw0 - temp);
+ var a0 = (a + 1) + (a - 1) * cosw0 + temp;
+ var a1 = -2 * ((a - 1) + (a + 1) * cosw0);
+ var a2 = (a + 1) + (a - 1) * cosw0 - temp;
+ return new BiQuadFilter(a0, a1, a2, b0, b1, b2);
+ }
+
+ ///
+ /// H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static BiQuadFilter HighShelf(float sampleRate, float cutoffFrequency, float shelfSlope, float dbGain)
+ {
+ var w0 = 2 * Math.PI * cutoffFrequency / sampleRate;
+ var cosw0 = Math.Cos(w0);
+ var sinw0 = Math.Sin(w0);
+ var a = Math.Pow(10, dbGain / 40); // TODO: should we square root this value?
+ var alpha = sinw0 / 2 * Math.Sqrt((a + 1 / a) * (1 / shelfSlope - 1) + 2);
+ var temp = 2 * Math.Sqrt(a) * alpha;
+
+ var b0 = a * ((a + 1) + (a - 1) * cosw0 + temp);
+ var b1 = -2 * a * ((a - 1) + (a + 1) * cosw0);
+ var b2 = a * ((a + 1) + (a - 1) * cosw0 - temp);
+ var a0 = (a + 1) - (a - 1) * cosw0 + temp;
+ var a1 = 2 * ((a - 1) - (a + 1) * cosw0);
+ var a2 = (a + 1) - (a - 1) * cosw0 - temp;
+ return new BiQuadFilter(a0, a1, a2, b0, b1, b2);
+ }
+
+ private BiQuadFilter()
+ {
+ // zero initial samples
+ x1 = x2 = 0;
+ y1 = y2 = 0;
+ }
+
+ private BiQuadFilter(double a0, double a1, double a2, double b0, double b1, double b2)
+ {
+ SetCoefficients(a0,a1,a2,b0,b1,b2);
+
+ // zero initial samples
+ x1 = x2 = 0;
+ y1 = y2 = 0;
+ }
+ }
+}
diff --git a/NAudio/Dsp/Complex.cs b/NAudio/Dsp/Complex.cs
new file mode 100644
index 00000000..f454e207
--- /dev/null
+++ b/NAudio/Dsp/Complex.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Dsp
+{
+ ///
+ /// Type to represent complex number
+ ///
+ public struct Complex
+ {
+ ///
+ /// Real Part
+ ///
+ public float X;
+ ///
+ /// Imaginary Part
+ ///
+ public float Y;
+ }
+}
diff --git a/NAudio/Dsp/EnvelopeDetector.cs b/NAudio/Dsp/EnvelopeDetector.cs
new file mode 100644
index 00000000..00a8e266
--- /dev/null
+++ b/NAudio/Dsp/EnvelopeDetector.cs
@@ -0,0 +1,108 @@
+// based on EnvelopeDetector.cpp v1.10 © 2006, ChunkWare Music Software, OPEN-SOURCE
+using System;
+
+namespace NAudio.Dsp
+{
+ class EnvelopeDetector
+ {
+ private double sampleRate;
+ private double ms;
+ private double coeff;
+
+ public EnvelopeDetector() : this(1.0, 44100.0)
+ {
+ }
+
+ public EnvelopeDetector( double ms, double sampleRate )
+ {
+ System.Diagnostics.Debug.Assert( sampleRate > 0.0 );
+ System.Diagnostics.Debug.Assert( ms > 0.0 );
+ this.sampleRate = sampleRate;
+ this.ms = ms;
+ SetCoef();
+ }
+
+ public double TimeConstant
+ {
+ get
+ {
+ return ms;
+ }
+ set
+ {
+ System.Diagnostics.Debug.Assert( value > 0.0 );
+ this.ms = value;
+ SetCoef();
+ }
+ }
+
+ public double SampleRate
+ {
+ get
+ {
+ return sampleRate;
+ }
+ set
+ {
+ System.Diagnostics.Debug.Assert( value > 0.0 );
+ this.sampleRate = value;
+ SetCoef();
+ }
+ }
+
+ public void Run( double inValue, ref double state )
+ {
+ state = inValue + coeff * (state - inValue);
+ }
+
+ private void SetCoef()
+ {
+ coeff = Math.Exp(-1.0 / (0.001 * ms * sampleRate));
+ }
+ }
+
+ class AttRelEnvelope
+ {
+ // DC offset to prevent denormal
+ protected const double DC_OFFSET = 1.0E-25;
+
+ private readonly EnvelopeDetector attack;
+ private readonly EnvelopeDetector release;
+
+ public AttRelEnvelope( double attackMilliseconds, double releaseMilliseconds, double sampleRate )
+ {
+ attack = new EnvelopeDetector(attackMilliseconds,sampleRate);
+ release = new EnvelopeDetector(releaseMilliseconds,sampleRate);
+ }
+
+ public double Attack
+ {
+ get { return attack.TimeConstant; }
+ set { attack.TimeConstant = value; }
+ }
+
+ public double Release
+ {
+ get { return release.TimeConstant; }
+ set { release.TimeConstant = value; }
+ }
+
+ public double SampleRate
+ {
+ get { return attack.SampleRate; }
+ set { attack.SampleRate = release.SampleRate = value; }
+ }
+
+ public void Run(double inValue, ref double state)
+ {
+ // assumes that:
+ // positive delta = attack
+ // negative delta = release
+ // good for linear & log values
+ if ( inValue > state )
+ attack.Run( inValue, ref state ); // attack
+ else
+ release.Run( inValue, ref state ); // release
+ }
+ }
+}
diff --git a/NAudio/Dsp/EnvelopeGenerator.cs b/NAudio/Dsp/EnvelopeGenerator.cs
new file mode 100644
index 00000000..7aa8d390
--- /dev/null
+++ b/NAudio/Dsp/EnvelopeGenerator.cs
@@ -0,0 +1,244 @@
+using System;
+using System.Linq;
+
+namespace NAudio.Dsp
+{
+ // C# ADSR based on work by Nigel Redmon, EarLevel Engineering: earlevel.com
+ // http://www.earlevel.com/main/2013/06/03/envelope-generators-adsr-code/
+ ///
+ /// Envelope generator (ADSR)
+ ///
+ public class EnvelopeGenerator
+ {
+ private EnvelopeState state;
+ private float output;
+ private float attackRate;
+ private float decayRate;
+ private float releaseRate;
+ private float attackCoef;
+ private float decayCoef;
+ private float releaseCoef;
+ private float sustainLevel;
+ private float targetRatioAttack;
+ private float targetRatioDecayRelease;
+ private float attackBase;
+ private float decayBase;
+ private float releaseBase;
+
+ ///
+ /// Envelope State
+ ///
+ public enum EnvelopeState
+ {
+ ///
+ /// Idle
+ ///
+ Idle = 0,
+ ///
+ /// Attack
+ ///
+ Attack,
+ ///
+ /// Decay
+ ///
+ Decay,
+ ///
+ /// Sustain
+ ///
+ Sustain,
+ ///
+ /// Release
+ ///
+ Release
+ };
+
+ ///
+ /// Creates and Initializes an Envelope Generator
+ ///
+ public EnvelopeGenerator()
+ {
+ Reset();
+ AttackRate = 0;
+ DecayRate = 0;
+ ReleaseRate = 0;
+ SustainLevel = 1.0f;
+ SetTargetRatioAttack(0.3f);
+ SetTargetRatioDecayRelease(0.0001f);
+ }
+
+ ///
+ /// Attack Rate (seconds * SamplesPerSecond)
+ ///
+ public float AttackRate
+ {
+ get
+ {
+ return attackRate;
+ }
+ set
+ {
+ attackRate = value;
+ attackCoef = CalcCoef(value, targetRatioAttack);
+ attackBase = (1.0f + targetRatioAttack) * (1.0f - attackCoef);
+ }
+ }
+
+ ///
+ /// Decay Rate (seconds * SamplesPerSecond)
+ ///
+ public float DecayRate
+ {
+ get
+ {
+ return decayRate;
+ }
+ set
+ {
+ decayRate = value;
+ decayCoef = CalcCoef(value, targetRatioDecayRelease);
+ decayBase = (sustainLevel - targetRatioDecayRelease) * (1.0f - decayCoef);
+ }
+ }
+
+ ///
+ /// Release Rate (seconds * SamplesPerSecond)
+ ///
+ public float ReleaseRate
+ {
+ get
+ {
+ return releaseRate;
+ }
+ set
+ {
+ releaseRate = value;
+ releaseCoef = CalcCoef(value, targetRatioDecayRelease);
+ releaseBase = -targetRatioDecayRelease * (1.0f - releaseCoef);
+ }
+ }
+
+ private static float CalcCoef(float rate, float targetRatio)
+ {
+ return (float)Math.Exp(-Math.Log((1.0f + targetRatio) / targetRatio) / rate);
+ }
+
+ ///
+ /// Sustain Level (1 = 100%)
+ ///
+ public float SustainLevel
+ {
+ get
+ {
+ return sustainLevel;
+ }
+ set
+ {
+ sustainLevel = value;
+ decayBase = (sustainLevel - targetRatioDecayRelease) * (1.0f - decayCoef);
+ }
+ }
+
+ ///
+ /// Sets the attack curve
+ ///
+ void SetTargetRatioAttack(float targetRatio)
+ {
+ if (targetRatio < 0.000000001f)
+ targetRatio = 0.000000001f; // -180 dB
+ targetRatioAttack = targetRatio;
+ attackBase = (1.0f + targetRatioAttack) * (1.0f - attackCoef);
+ }
+
+ ///
+ /// Sets the decay release curve
+ ///
+ void SetTargetRatioDecayRelease(float targetRatio)
+ {
+ if (targetRatio < 0.000000001f)
+ targetRatio = 0.000000001f; // -180 dB
+ targetRatioDecayRelease = targetRatio;
+ decayBase = (sustainLevel - targetRatioDecayRelease) * (1.0f - decayCoef);
+ releaseBase = -targetRatioDecayRelease * (1.0f - releaseCoef);
+ }
+
+ ///
+ /// Read the next volume multiplier from the envelope generator
+ ///
+ /// A volume multiplier
+ public float Process()
+ {
+ switch (state)
+ {
+ case EnvelopeState.Idle:
+ break;
+ case EnvelopeState.Attack:
+ output = attackBase + output * attackCoef;
+ if (output >= 1.0f)
+ {
+ output = 1.0f;
+ state = EnvelopeState.Decay;
+ }
+ break;
+ case EnvelopeState.Decay:
+ output = decayBase + output * decayCoef;
+ if (output <= sustainLevel)
+ {
+ output = sustainLevel;
+ state = EnvelopeState.Sustain;
+ }
+ break;
+ case EnvelopeState.Sustain:
+ break;
+ case EnvelopeState.Release:
+ output = releaseBase + output * releaseCoef;
+ if (output <= 0.0)
+ {
+ output = 0.0f;
+ state = EnvelopeState.Idle;
+ }
+ break;
+ }
+ return output;
+ }
+
+ ///
+ /// Trigger the gate
+ ///
+ /// If true, enter attack phase, if false enter release phase (unless already idle)
+ public void Gate(bool gate)
+ {
+ if (gate)
+ state = EnvelopeState.Attack;
+ else if (state != EnvelopeState.Idle)
+ state = EnvelopeState.Release;
+ }
+
+ ///
+ /// Current envelope state
+ ///
+ public EnvelopeState State
+ {
+ get
+ {
+ return state;
+ }
+ }
+
+ ///
+ /// Reset to idle state
+ ///
+ public void Reset()
+ {
+ state = EnvelopeState.Idle;
+ output = 0.0f;
+ }
+
+ ///
+ /// Get the current output level
+ ///
+ public float GetOutput()
+ {
+ return output;
+ }
+ }
+}
diff --git a/NAudio/Dsp/FastFourierTransform.cs b/NAudio/Dsp/FastFourierTransform.cs
new file mode 100644
index 00000000..fe9ddc9d
--- /dev/null
+++ b/NAudio/Dsp/FastFourierTransform.cs
@@ -0,0 +1,124 @@
+using System;
+
+namespace NAudio.Dsp
+{
+ ///
+ /// Summary description for FastFourierTransform.
+ ///
+ public static class FastFourierTransform
+ {
+ ///
+ /// This computes an in-place complex-to-complex FFT
+ /// x and y are the real and imaginary arrays of 2^m points.
+ ///
+ public static void FFT(bool forward, int m, Complex[] data)
+ {
+ int n, i, i1, j, k, i2, l, l1, l2;
+ float c1, c2, tx, ty, t1, t2, u1, u2, z;
+
+ // Calculate the number of points
+ n = 1;
+ for (i = 0; i < m; i++)
+ n *= 2;
+
+ // Do the bit reversal
+ i2 = n >> 1;
+ j = 0;
+ for (i = 0; i < n - 1; i++)
+ {
+ if (i < j)
+ {
+ tx = data[i].X;
+ ty = data[i].Y;
+ data[i].X = data[j].X;
+ data[i].Y = data[j].Y;
+ data[j].X = tx;
+ data[j].Y = ty;
+ }
+ k = i2;
+
+ while (k <= j)
+ {
+ j -= k;
+ k >>= 1;
+ }
+ j += k;
+ }
+
+ // Compute the FFT
+ c1 = -1.0f;
+ c2 = 0.0f;
+ l2 = 1;
+ for (l = 0; l < m; l++)
+ {
+ l1 = l2;
+ l2 <<= 1;
+ u1 = 1.0f;
+ u2 = 0.0f;
+ for (j = 0; j < l1; j++)
+ {
+ for (i = j; i < n; i += l2)
+ {
+ i1 = i + l1;
+ t1 = u1 * data[i1].X - u2 * data[i1].Y;
+ t2 = u1 * data[i1].Y + u2 * data[i1].X;
+ data[i1].X = data[i].X - t1;
+ data[i1].Y = data[i].Y - t2;
+ data[i].X += t1;
+ data[i].Y += t2;
+ }
+ z = u1 * c1 - u2 * c2;
+ u2 = u1 * c2 + u2 * c1;
+ u1 = z;
+ }
+ c2 = (float)Math.Sqrt((1.0f - c1) / 2.0f);
+ if (forward)
+ c2 = -c2;
+ c1 = (float)Math.Sqrt((1.0f + c1) / 2.0f);
+ }
+
+ // Scaling for forward transform
+ if (forward)
+ {
+ for (i = 0; i < n; i++)
+ {
+ data[i].X /= n;
+ data[i].Y /= n;
+ }
+ }
+ }
+
+ ///
+ /// Applies a Hamming Window
+ ///
+ /// Index into frame
+ /// Frame size (e.g. 1024)
+ /// Multiplier for Hamming window
+ public static double HammingWindow(int n, int frameSize)
+ {
+ return 0.54 - 0.46 * Math.Cos((2 * Math.PI * n) / (frameSize - 1));
+ }
+
+ ///
+ /// Applies a Hann Window
+ ///
+ /// Index into frame
+ /// Frame size (e.g. 1024)
+ /// Multiplier for Hann window
+ public static double HannWindow(int n, int frameSize)
+ {
+ return 0.5 * (1 - Math.Cos((2 * Math.PI * n) / (frameSize - 1)));
+ }
+
+ ///
+ /// Applies a Blackman-Harris Window
+ ///
+ /// Index into frame
+ /// Frame size (e.g. 1024)
+ /// Multiplier for Blackmann-Harris window
+ public static double BlackmannHarrisWindow(int n, int frameSize)
+ {
+ return 0.35875 - (0.48829 * Math.Cos((2 * Math.PI * n) / (frameSize - 1))) + (0.14128 * Math.Cos((4 * Math.PI * n) / (frameSize - 1))) - (0.01168 * Math.Cos((6 * Math.PI * n) / (frameSize - 1)));
+ }
+ }
+}
diff --git a/NAudio/Dsp/ImpulseResponseConvolution.cs b/NAudio/Dsp/ImpulseResponseConvolution.cs
new file mode 100644
index 00000000..daa78d66
--- /dev/null
+++ b/NAudio/Dsp/ImpulseResponseConvolution.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace NAudio.Dsp
+{
+ ///
+ /// Summary description for ImpulseResponseConvolution.
+ ///
+ public class ImpulseResponseConvolution
+ {
+ ///
+ /// A very simple mono convolution algorithm
+ ///
+ ///
+ /// This will be very slow
+ ///
+ public float[] Convolve(float[] input, float[] impulseResponse)
+ {
+ var output = new float[input.Length + impulseResponse.Length];
+ for(int t = 0; t < output.Length; t++)
+ {
+ for(int n = 0; n < impulseResponse.Length; n++)
+ {
+ if((t >= n) && (t-n < input.Length))
+ {
+ output[t] += impulseResponse[n] * input[t-n];
+ }
+ }
+ }
+ Normalize(output);
+ return output;
+ }
+
+ ///
+ /// This is actually a downwards normalize for data that will clip
+ ///
+ public void Normalize(float[] data)
+ {
+ float max = 0;
+ for(int n = 0; n < data.Length; n++)
+ max = Math.Max(max,Math.Abs(data[n]));
+ if(max > 1.0)
+ for(int n = 0; n < data.Length; n++)
+ data[n] /= max;
+ }
+ }
+}
diff --git a/NAudio/Dsp/SimpleCompressor.cs b/NAudio/Dsp/SimpleCompressor.cs
new file mode 100644
index 00000000..b5bd332e
--- /dev/null
+++ b/NAudio/Dsp/SimpleCompressor.cs
@@ -0,0 +1,89 @@
+// based on SimpleComp v1.10 © 2006, ChunkWare Music Software, OPEN-SOURCE
+using System;
+using NAudio.Utils;
+
+namespace NAudio.Dsp
+{
+ class SimpleCompressor : AttRelEnvelope
+ {
+ // transfer function
+
+ // runtime variables
+ private double envdB; // over-threshold envelope (dB)
+
+ public SimpleCompressor(double attackTime, double releaseTime, double sampleRate)
+ : base(attackTime, releaseTime, sampleRate)
+ {
+ this.Threshold = 0.0;
+ this.Ratio = 1.0;
+ this.MakeUpGain = 0.0;
+ this.envdB = DC_OFFSET;
+ }
+
+ public SimpleCompressor()
+ : base(10.0, 10.0, 44100.0)
+ {
+ this.Threshold = 0.0;
+ this.Ratio = 1.0;
+ this.MakeUpGain = 0.0;
+ this.envdB = DC_OFFSET;
+ }
+
+ public double MakeUpGain { get; set; }
+
+ public double Threshold { get; set; }
+
+ public double Ratio { get; set; }
+
+ // call before runtime (in resume())
+ public void InitRuntime()
+ {
+ this.envdB = DC_OFFSET;
+ }
+
+ // // compressor runtime process
+ public void Process(ref double in1, ref double in2)
+ {
+ // sidechain
+
+ // rectify input
+ double rect1 = Math.Abs( in1 ); // n.b. was fabs
+ double rect2 = Math.Abs( in2 ); // n.b. was fabs
+
+ // if desired, one could use another EnvelopeDetector to smooth
+ // the rectified signal.
+
+ double link = Math.Max( rect1, rect2 ); // link channels with greater of 2
+
+ link += DC_OFFSET; // add DC offset to avoid log( 0 )
+ double keydB = Decibels.LinearToDecibels( link ); // convert linear -> dB
+
+ // threshold
+ double overdB = keydB - Threshold; // delta over threshold
+ if ( overdB < 0.0 )
+ overdB = 0.0;
+
+ // attack/release
+
+ overdB += DC_OFFSET; // add DC offset to avoid denormal
+
+ Run( overdB, ref envdB ); // run attack/release envelope
+
+ overdB = envdB - DC_OFFSET; // subtract DC offset
+
+ // Regarding the DC offset: In this case, since the offset is added before
+ // the attack/release processes, the envelope will never fall below the offset,
+ // thereby avoiding denormals. However, to prevent the offset from causing
+ // constant gain reduction, we must subtract it from the envelope, yielding
+ // a minimum value of 0dB.
+
+ // transfer function
+ double gr = overdB * (Ratio - 1.0); // gain reduction (dB)
+ gr = Decibels.DecibelsToLinear(gr) * Decibels.DecibelsToLinear(MakeUpGain); // convert dB -> linear
+
+ // output gain
+ in1 *= gr; // apply gain reduction to input
+ in2 *= gr;
+ }
+ }
+}
diff --git a/NAudio/Dsp/SimpleGate.cs b/NAudio/Dsp/SimpleGate.cs
new file mode 100644
index 00000000..9cf886e8
--- /dev/null
+++ b/NAudio/Dsp/SimpleGate.cs
@@ -0,0 +1,73 @@
+// based on SimpleGate v1.10 © 2006, ChunkWare Music Software, OPEN-SOURCE
+using System;
+using NAudio.Utils;
+
+namespace NAudio.Dsp
+{
+ class SimpleGate : AttRelEnvelope
+ {
+ // transfer function
+ private double threshdB; // threshold (dB)
+ private double thresh; // threshold (linear)
+
+ // runtime variables
+ private double env; // over-threshold envelope (linear)
+
+ public SimpleGate()
+ : base(10.0, 10.0, 44100.0)
+ {
+ threshdB = 0.0;
+ thresh = 1.0;
+ env = DC_OFFSET;
+ }
+
+ public void Process( ref double in1, ref double in2 )
+ {
+ // in/out pointers are assummed to reference stereo data
+
+ // sidechain
+
+ // rectify input
+ double rect1 = Math.Abs( in1 ); // n.b. was fabs
+ double rect2 = Math.Abs( in2 ); // n.b. was fabs
+
+ // if desired, one could use another EnvelopeDetector to smooth
+ // the rectified signal.
+
+ double key = Math.Max( rect1, rect2 ); // link channels with greater of 2
+
+ // threshold
+ double over = ( key > thresh ) ? 1.0 : 0.0; // key over threshold ( 0.0 or 1.0 )
+
+ // attack/release
+ over += DC_OFFSET; // add DC offset to avoid denormal
+
+ Run( over, ref env ); // run attack/release
+
+ over = env - DC_OFFSET; // subtract DC offset
+
+ // Regarding the DC offset: In this case, since the offset is added before
+ // the attack/release processes, the envelope will never fall below the offset,
+ // thereby avoiding denormals. However, to prevent the offset from causing
+ // constant gain reduction, we must subtract it from the envelope, yielding
+ // a minimum value of 0dB.
+
+ // output gain
+ in1 *= over; // apply gain reduction to input
+ in2 *= over;
+ }
+
+ public double Threshold
+ {
+ get
+ {
+ return threshdB;
+ }
+ set
+ {
+ threshdB = value;
+ thresh = Decibels.DecibelsToLinear(value);
+ }
+ }
+ }
+}
diff --git a/NAudio/Dsp/WdlResampler.cs b/NAudio/Dsp/WdlResampler.cs
new file mode 100644
index 00000000..a25b2bfe
--- /dev/null
+++ b/NAudio/Dsp/WdlResampler.cs
@@ -0,0 +1,708 @@
+// This class based on the Resampler that is part of Cockos WDL
+// originally written in C++ and ported to C# for NAudio by Mark Heath
+// Used in NAudio with permission from Justin Frankel
+// Original WDL License:
+// Copyright (C) 2005 and later Cockos Incorporated
+//
+// Portions copyright other contributors, see each source file for more information
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+
+using System;
+using System.Diagnostics;
+using System.Linq;
+
+// default to floats for audio samples
+using WDL_ResampleSample = System.Single; // n.b. default in WDL is double
+
+// default to floats for sinc filter ceofficients
+using WDL_SincFilterSample = System.Single; // can also be set to double
+
+namespace NAudio.Dsp
+{
+ ///
+ /// Fully managed resampler, based on Cockos WDL Resampler
+ ///
+ class WdlResampler
+ {
+ private const int WDL_RESAMPLE_MAX_FILTERS = 4;
+ private const int WDL_RESAMPLE_MAX_NCH = 64;
+ private const double PI = 3.1415926535897932384626433832795;
+
+ ///
+ /// Creates a new Resampler
+ ///
+ public WdlResampler()
+ {
+ m_filterq = 0.707f;
+ m_filterpos = 0.693f; // .792 ?
+
+ m_sincoversize = 0;
+ m_lp_oversize = 1;
+ m_sincsize = 0;
+ m_filtercnt = 1;
+ m_interp = true;
+ m_feedmode = false;
+
+ m_filter_coeffs_size = 0;
+ m_sratein = 44100.0;
+ m_srateout = 44100.0;
+ m_ratio = 1.0;
+ m_filter_ratio = -1.0;
+
+ Reset();
+ }
+
+ ///
+ /// sets the mode
+ /// if sinc set, it overrides interp or filtercnt
+ ///
+ public void SetMode(bool interp, int filtercnt, bool sinc, int sinc_size = 64, int sinc_interpsize = 32)
+ {
+ m_sincsize = sinc && sinc_size >= 4 ? sinc_size > 8192 ? 8192 : sinc_size : 0;
+ m_sincoversize = (m_sincsize != 0) ? (sinc_interpsize <= 1 ? 1 : sinc_interpsize >= 4096 ? 4096 : sinc_interpsize) : 1;
+
+ m_filtercnt = (m_sincsize != 0) ? 0 : (filtercnt <= 0 ? 0 : filtercnt >= WDL_RESAMPLE_MAX_FILTERS ? WDL_RESAMPLE_MAX_FILTERS : filtercnt);
+ m_interp = interp && (m_sincsize == 0);
+
+ //Debug.WriteLine(String.Format("setting interp={0}, filtercnt={1}, sinc={2},{3}\n", m_interp, m_filtercnt, m_sincsize, m_sincoversize));
+
+ if (m_sincsize == 0)
+ {
+ m_filter_coeffs = new WDL_SincFilterSample[0]; //.Resize(0);
+ m_filter_coeffs_size = 0;
+ }
+ if (m_filtercnt == 0)
+ {
+ m_iirfilter = null;
+ }
+ }
+
+ ///
+ /// Sets the filter parameters
+ /// used for filtercnt>0 but not sinc
+ ///
+ public void SetFilterParms(float filterpos = 0.693f, float filterq = 0.707f)
+ {
+ m_filterpos = filterpos;
+ m_filterq = filterq;
+ }
+
+ ///
+ /// Set feed mode
+ ///
+ /// if true, that means the first parameter to ResamplePrepare will specify however much input you have, not how much you want
+ public void SetFeedMode(bool wantInputDriven)
+ {
+ m_feedmode = wantInputDriven;
+ }
+
+ ///
+ /// Reset
+ ///
+ public void Reset(double fracpos = 0.0)
+ {
+ m_last_requested = 0;
+ m_filtlatency = 0;
+ m_fracpos = fracpos;
+ m_samples_in_rsinbuf = 0;
+ if (m_iirfilter != null) m_iirfilter.Reset();
+ }
+
+ public void SetRates(double rate_in, double rate_out)
+ {
+ if (rate_in < 1.0) rate_in = 1.0;
+ if (rate_out < 1.0) rate_out = 1.0;
+ if (rate_in != m_sratein || rate_out != m_srateout)
+ {
+ m_sratein = rate_in;
+ m_srateout = rate_out;
+ m_ratio = m_sratein / m_srateout;
+ }
+ }
+
+ // amount of input that has been received but not yet converted to output, in seconds
+ public double GetCurrentLatency()
+ {
+ double v = ((double)m_samples_in_rsinbuf - m_filtlatency) / m_sratein;
+
+ if (v < 0.0) v = 0.0;
+ return v;
+ }
+
+ ///
+ /// Prepare
+ /// note that it is safe to call ResamplePrepare without calling ResampleOut (the next call of ResamplePrepare will function as normal)
+ /// nb inbuffer was WDL_ResampleSample **, returning a place to put the in buffer, so we return a buffer and offset
+ ///
+ /// req_samples is output samples desired if !wantInputDriven, or if wantInputDriven is input samples that we have
+ ///
+ ///
+ ///
+ /// returns number of samples desired (put these into *inbuffer)
+ public int ResamplePrepare(int out_samples, int nch, out WDL_ResampleSample[] inbuffer, out int inbufferOffset)
+ {
+ if (nch > WDL_RESAMPLE_MAX_NCH || nch < 1)
+ {
+ inbuffer = null;
+ inbufferOffset = 0;
+ return 0;
+ }
+
+ int fsize = 0;
+ if (m_sincsize > 1)
+ {
+ fsize = m_sincsize;
+ }
+
+ int hfs = fsize / 2;
+ if (hfs > 1 && m_samples_in_rsinbuf < hfs - 1)
+ {
+ m_filtlatency += hfs - 1 - m_samples_in_rsinbuf;
+
+ m_samples_in_rsinbuf = hfs - 1;
+
+ if (m_samples_in_rsinbuf > 0)
+ {
+ m_rsinbuf = new WDL_SincFilterSample[m_samples_in_rsinbuf * nch];
+ }
+ }
+
+ int sreq = 0;
+
+ if (!m_feedmode) sreq = (int)(m_ratio * out_samples) + 4 + fsize - m_samples_in_rsinbuf;
+ else sreq = out_samples;
+
+ if (sreq < 0) sreq = 0;
+
+ again:
+ Array.Resize(ref m_rsinbuf, (m_samples_in_rsinbuf + sreq) * nch);
+
+ int sz = m_rsinbuf.Length / ((nch != 0) ? nch : 1) - m_samples_in_rsinbuf;
+ if (sz != sreq)
+ {
+ if (sreq > 4 && (sz == 0))
+ {
+ sreq /= 2;
+ goto again; // try again with half the size
+ }
+ // todo: notify of error?
+ sreq = sz;
+ }
+
+ inbuffer = m_rsinbuf;
+ inbufferOffset = m_samples_in_rsinbuf * nch;
+
+ m_last_requested = sreq;
+ return sreq;
+ }
+
+ // if numsamples_in < the value return by ResamplePrepare(), then it will be flushed to produce all remaining valid samples
+ // do NOT call with nsamples_in greater than the value returned from resamplerprpare()! the extra samples will be ignored.
+ // returns number of samples successfully outputted to out
+ public int ResampleOut(WDL_ResampleSample[] outBuffer, int outBufferIndex, int nsamples_in, int nsamples_out, int nch)
+ {
+ if (nch > WDL_RESAMPLE_MAX_NCH || nch < 1)
+ {
+ return 0;
+ }
+
+ if (m_filtercnt > 0)
+ {
+ if (m_ratio > 1.0 && nsamples_in > 0) // filter input
+ {
+ if (m_iirfilter == null) m_iirfilter = new WDL_Resampler_IIRFilter();
+
+ int n = m_filtercnt;
+ m_iirfilter.setParms((1.0 / m_ratio) * m_filterpos, m_filterq);
+
+ int bufIndex = m_samples_in_rsinbuf * nch;
+ int a, x;
+ int offs = 0;
+ for (x = 0; x < nch; x++)
+ for (a = 0; a < n; a++)
+ m_iirfilter.Apply(m_rsinbuf, bufIndex + x, m_rsinbuf, bufIndex + x, nsamples_in, nch, offs++);
+ }
+ }
+
+ m_samples_in_rsinbuf += Math.Min(nsamples_in, m_last_requested); // prevent the user from corrupting the internal state
+
+
+ int rsinbuf_availtemp = m_samples_in_rsinbuf;
+
+ if (nsamples_in < m_last_requested) // flush out to ensure we can deliver
+ {
+ int fsize = (m_last_requested - nsamples_in) * 2 + m_sincsize * 2;
+
+ int alloc_size = (m_samples_in_rsinbuf + fsize) * nch;
+ Array.Resize(ref m_rsinbuf, alloc_size);
+ if (m_rsinbuf.Length == alloc_size)
+ {
+ Array.Clear(m_rsinbuf, m_samples_in_rsinbuf * nch, fsize * nch);
+ rsinbuf_availtemp = m_samples_in_rsinbuf + fsize;
+ }
+ }
+
+ int ret = 0;
+ double srcpos = m_fracpos;
+ double drspos = m_ratio;
+ int localin = 0; // localin is an index into m_rsinbuf
+
+ int outptr = outBufferIndex; // outptr is an index into outBuffer;
+
+ int ns = nsamples_out;
+
+ int outlatadj = 0;
+
+ if (m_sincsize != 0) // sinc interpolating
+ {
+ if (m_ratio > 1.0) BuildLowPass(1.0 / (m_ratio * 1.03));
+ else BuildLowPass(1.0);
+
+ int filtsz = m_filter_coeffs_size;
+ int filtlen = rsinbuf_availtemp - filtsz;
+ outlatadj = filtsz / 2 - 1;
+ int filter = 0; // filter is an index into m_filter_coeffs m_filter_coeffs.Get();
+
+ if (nch == 1)
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+
+ if (ipos >= filtlen - 1) break; // quit decoding, not enough input samples
+
+ SincSample1(outBuffer, outptr, m_rsinbuf, localin + ipos, srcpos - ipos, m_filter_coeffs, filter, filtsz);
+ outptr++;
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ else if (nch == 2)
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+
+ if (ipos >= filtlen - 1) break; // quit decoding, not enough input samples
+
+ SincSample2(outBuffer, outptr, m_rsinbuf, localin + ipos * 2, srcpos - ipos, m_filter_coeffs, filter, filtsz);
+ outptr += 2;
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ else
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+
+ if (ipos >= filtlen - 1) break; // quit decoding, not enough input samples
+
+ SincSample(outBuffer, outptr, m_rsinbuf, localin + ipos * nch, srcpos - ipos, nch, m_filter_coeffs, filter, filtsz);
+ outptr += nch;
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ }
+ else if (!m_interp) // point sampling
+ {
+ if (nch == 1)
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+ if (ipos >= rsinbuf_availtemp) break; // quit decoding, not enough input samples
+
+ outBuffer[outptr++] = m_rsinbuf[localin + ipos];
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ else if (nch == 2)
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+ if (ipos >= rsinbuf_availtemp) break; // quit decoding, not enough input samples
+
+ ipos += ipos;
+
+ outBuffer[outptr + 0] = m_rsinbuf[localin + ipos];
+ outBuffer[outptr + 1] = m_rsinbuf[localin + ipos + 1];
+ outptr += 2;
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ else
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+ if (ipos >= rsinbuf_availtemp) break; // quit decoding, not enough input samples
+
+ Array.Copy(m_rsinbuf, localin + ipos * nch, outBuffer, outptr, nch);
+ outptr += nch;
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ else // linear interpolation
+ {
+ if (nch == 1)
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+ double fracpos = srcpos - ipos;
+
+ if (ipos >= rsinbuf_availtemp - 1)
+ {
+ break; // quit decoding, not enough input samples
+ }
+
+ double ifracpos = 1.0 - fracpos;
+ int inptr = localin + ipos;
+ outBuffer[outptr++] = (WDL_ResampleSample)(m_rsinbuf[inptr] * (ifracpos) + m_rsinbuf[inptr + 1] * (fracpos));
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ else if (nch == 2)
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+ double fracpos = srcpos - ipos;
+
+ if (ipos >= rsinbuf_availtemp - 1)
+ {
+ break; // quit decoding, not enough input samples
+ }
+
+ double ifracpos = 1.0 - fracpos;
+ int inptr = localin + ipos * 2;
+ outBuffer[outptr + 0] = (WDL_ResampleSample)(m_rsinbuf[inptr] * (ifracpos) + m_rsinbuf[inptr + 2] * (fracpos));
+ outBuffer[outptr + 1] = (WDL_ResampleSample)(m_rsinbuf[inptr + 1] * (ifracpos) + m_rsinbuf[inptr + 3] * (fracpos));
+ outptr += 2;
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ else
+ {
+ while (ns-- != 0)
+ {
+ int ipos = (int)srcpos;
+ double fracpos = srcpos - ipos;
+
+ if (ipos >= rsinbuf_availtemp - 1)
+ {
+ break; // quit decoding, not enough input samples
+ }
+
+ double ifracpos = 1.0 - fracpos;
+ int ch = nch;
+ int inptr = localin + ipos * nch;
+ while (ch-- != 0)
+ {
+ outBuffer[outptr++] = (WDL_ResampleSample)(m_rsinbuf[inptr] * (ifracpos) + m_rsinbuf[inptr + nch] * (fracpos));
+ inptr++;
+ }
+ srcpos += drspos;
+ ret++;
+ }
+ }
+ }
+
+ if (m_filtercnt > 0)
+ {
+ if (m_ratio < 1.0 && ret > 0) // filter output
+ {
+ if (m_iirfilter == null) m_iirfilter = new WDL_Resampler_IIRFilter();
+ int n = m_filtercnt;
+ m_iirfilter.setParms(m_ratio * m_filterpos, m_filterq);
+
+ int x, a;
+ int offs = 0;
+ for (x = 0; x < nch; x++)
+ for (a = 0; a < n; a++)
+ m_iirfilter.Apply(outBuffer, x, outBuffer, x, ret, nch, offs++);
+ }
+ }
+
+ if (ret > 0 && rsinbuf_availtemp > m_samples_in_rsinbuf) // we had to pad!!
+ {
+ // check for the case where rsinbuf_availtemp>m_samples_in_rsinbuf, decrease ret down to actual valid samples
+ double adj = (srcpos - m_samples_in_rsinbuf + outlatadj) / drspos;
+ if (adj > 0)
+ {
+ ret -= (int)(adj + 0.5);
+ if (ret < 0) ret = 0;
+ }
+ }
+
+ int isrcpos = (int)srcpos;
+ m_fracpos = srcpos - isrcpos;
+ m_samples_in_rsinbuf -= isrcpos;
+ if (m_samples_in_rsinbuf <= 0)
+ {
+ m_samples_in_rsinbuf = 0;
+ }
+ else
+ {
+ // TODO: bug here
+ Array.Copy(m_rsinbuf, localin + isrcpos * nch, m_rsinbuf, localin, m_samples_in_rsinbuf * nch);
+ }
+
+
+
+ return ret;
+ }
+
+ // only called in sinc modes
+ private void BuildLowPass(double filtpos)
+ {
+ int wantsize = m_sincsize;
+ int wantinterp = m_sincoversize;
+
+ if (m_filter_ratio != filtpos ||
+ m_filter_coeffs_size != wantsize ||
+ m_lp_oversize != wantinterp)
+ {
+ m_lp_oversize = wantinterp;
+ m_filter_ratio = filtpos;
+
+ // build lowpass filter
+ int allocsize = (wantsize + 1) * m_lp_oversize;
+ Array.Resize(ref m_filter_coeffs, allocsize);
+ //int cfout = 0; // this is an index into m_filter_coeffs
+ if (m_filter_coeffs.Length == allocsize)
+ {
+ m_filter_coeffs_size = wantsize;
+
+ int sz = wantsize * m_lp_oversize;
+ int hsz = sz / 2;
+ double filtpower = 0.0;
+ double windowpos = 0.0;
+ double dwindowpos = 2.0 * PI / (double)(sz);
+ double dsincpos = PI / m_lp_oversize * filtpos; // filtpos is outrate/inrate, i.e. 0.5 is going to half rate
+ double sincpos = dsincpos * (double)(-hsz);
+
+ int x;
+ for (x = -hsz; x < hsz + m_lp_oversize; x++)
+ {
+ double val = 0.35875 - 0.48829 * Math.Cos(windowpos) + 0.14128 * Math.Cos(2 * windowpos) - 0.01168 * Math.Cos(6 * windowpos); // blackman-harris
+ if (x != 0) val *= Math.Sin(sincpos) / sincpos;
+
+ windowpos += dwindowpos;
+ sincpos += dsincpos;
+
+ m_filter_coeffs[hsz + x] = (WDL_SincFilterSample)val;
+ if (x < hsz) filtpower += val;
+ }
+ filtpower = m_lp_oversize / filtpower;
+ for (x = 0; x < sz + m_lp_oversize; x++)
+ {
+ m_filter_coeffs[x] = (WDL_SincFilterSample)(m_filter_coeffs[x] * filtpower);
+ }
+ }
+ else m_filter_coeffs_size = 0;
+
+ }
+ }
+
+ // SincSample(WDL_ResampleSample *outptr, WDL_ResampleSample *inptr, double fracpos, int nch, WDL_SincFilterSample *filter, int filtsz)
+ private void SincSample(WDL_ResampleSample[] outBuffer, int outBufferIndex, WDL_ResampleSample[] inBuffer, int inBufferIndex, double fracpos, int nch, WDL_SincFilterSample[] filter, int filterIndex, int filtsz)
+ {
+ int oversize = m_lp_oversize;
+ fracpos *= oversize;
+ int ifpos = (int)fracpos;
+ filterIndex += oversize - 1 - ifpos;
+ fracpos -= ifpos;
+
+ for (int x = 0; x < nch; x++)
+ {
+ double sum = 0.0, sum2 = 0.0;
+ int fptr = filterIndex;
+ int iptr = inBufferIndex + x;
+ int i = filtsz;
+ while (i-- != 0)
+ {
+ sum += filter[fptr] * inBuffer[iptr];
+ sum2 += filter[fptr + 1] * inBuffer[iptr];
+ iptr += nch;
+ fptr += oversize;
+ }
+ outBuffer[outBufferIndex + x] = (WDL_ResampleSample)(sum * fracpos + sum2 * (1.0 - fracpos));
+ }
+ }
+
+ // SincSample1(WDL_ResampleSample* outptr, WDL_ResampleSample* inptr, double fracpos, WDL_SincFilterSample* filter, int filtsz)
+ private void SincSample1(WDL_ResampleSample[] outBuffer, int outBufferIndex, WDL_ResampleSample[] inBuffer, int inBufferIndex, double fracpos, WDL_SincFilterSample[] filter, int filterIndex, int filtsz)
+ {
+ int oversize = m_lp_oversize;
+ fracpos *= oversize;
+ int ifpos = (int)fracpos;
+ filterIndex += oversize - 1 - ifpos;
+ fracpos -= ifpos;
+
+ double sum = 0.0, sum2 = 0.0;
+ int fptr = filterIndex;
+ int iptr = inBufferIndex;
+ int i = filtsz;
+ while (i-- != 0)
+ {
+ sum += filter[fptr] * inBuffer[iptr];
+ sum2 += filter[fptr + 1] * inBuffer[iptr];
+ iptr++;
+ fptr += oversize;
+ }
+ outBuffer[outBufferIndex] = (WDL_ResampleSample)(sum * fracpos + sum2 * (1.0 - fracpos));
+ }
+
+ // SincSample2(WDL_ResampleSample* outptr, WDL_ResampleSample* inptr, double fracpos, WDL_SincFilterSample* filter, int filtsz)
+ private void SincSample2(WDL_ResampleSample[] outptr, int outBufferIndex, WDL_ResampleSample[] inBuffer, int inBufferIndex, double fracpos, WDL_SincFilterSample[] filter, int filterIndex, int filtsz)
+ {
+ int oversize = m_lp_oversize;
+ fracpos *= oversize;
+ int ifpos = (int)fracpos;
+ filterIndex += oversize - 1 - ifpos;
+ fracpos -= ifpos;
+
+ double sum = 0.0;
+ double sum2 = 0.0;
+ double sumb = 0.0;
+ double sum2b = 0.0;
+ int fptr = filterIndex;
+ int iptr = inBufferIndex;
+ int i = filtsz / 2;
+ while (i-- != 0)
+ {
+ sum += filter[fptr] * inBuffer[iptr];
+ sum2 += filter[fptr] * inBuffer[iptr + 1];
+ sumb += filter[fptr + 1] * inBuffer[iptr];
+ sum2b += filter[fptr + 1] * inBuffer[iptr + 1];
+ sum += filter[fptr + oversize] * inBuffer[iptr + 2];
+ sum2 += filter[fptr + oversize] * inBuffer[iptr + 3];
+ sumb += filter[fptr + oversize + 1] * inBuffer[iptr + 2];
+ sum2b += filter[fptr + oversize + 1] * inBuffer[iptr + 3];
+ iptr += 4;
+ fptr += oversize * 2;
+ }
+ outptr[outBufferIndex + 0] = (WDL_ResampleSample)(sum * fracpos + sumb * (1.0 - fracpos));
+ outptr[outBufferIndex + 1] = (WDL_ResampleSample)(sum2 * fracpos + sum2b * (1.0 - fracpos));
+ }
+
+ private double m_sratein; // WDL_FIXALIGN
+ private double m_srateout;
+ private double m_fracpos;
+ private double m_ratio;
+ private double m_filter_ratio;
+ private float m_filterq, m_filterpos;
+ private WDL_ResampleSample[] m_rsinbuf; // WDL_TypedBuf
+ private WDL_SincFilterSample[] m_filter_coeffs; // WDL_TypedBuf
+
+ private WDL_Resampler_IIRFilter m_iirfilter; // WDL_Resampler_IIRFilter *
+
+ private int m_filter_coeffs_size;
+ private int m_last_requested;
+ private int m_filtlatency;
+ private int m_samples_in_rsinbuf;
+ private int m_lp_oversize;
+
+ private int m_sincsize;
+ private int m_filtercnt;
+ private int m_sincoversize;
+ private bool m_interp;
+ private bool m_feedmode;
+
+
+
+ class WDL_Resampler_IIRFilter
+ {
+ public WDL_Resampler_IIRFilter()
+ {
+ m_fpos = -1;
+ Reset();
+ }
+
+ public void Reset()
+ {
+ m_hist = new double[WDL_RESAMPLE_MAX_FILTERS * WDL_RESAMPLE_MAX_NCH, 4];
+ }
+
+ public void setParms(double fpos, double Q)
+ {
+ if (Math.Abs(fpos - m_fpos) < 0.000001) return;
+ m_fpos = fpos;
+
+ double pos = fpos * PI;
+ double cpos = Math.Cos(pos);
+ double spos = Math.Sin(pos);
+
+ double alpha = spos / (2.0 * Q);
+
+ double sc = 1.0 / (1 + alpha);
+ m_b1 = (1 - cpos) * sc;
+ m_b2 = m_b0 = m_b1 * 0.5;
+ m_a1 = -2 * cpos * sc;
+ m_a2 = (1 - alpha) * sc;
+
+ }
+
+ public void Apply(WDL_ResampleSample[] inBuffer, int inIndex, WDL_ResampleSample[] outBuffer, int outIndex, int ns, int span, int w)
+ {
+ double b0 = m_b0, b1 = m_b1, b2 = m_b2, a1 = m_a1, a2 = m_a2;
+
+ while (ns-- != 0)
+ {
+ double inx = inBuffer[inIndex];
+ inIndex += span;
+ double outx = (double)(inx * b0 + m_hist[w, 0] * b1 + m_hist[w, 1] * b2 - m_hist[w, 2] * a1 - m_hist[w, 3] * a2);
+ m_hist[w, 1] = m_hist[w, 0];
+ m_hist[w, 0] = inx;
+ m_hist[w, 3] = m_hist[w, 2];
+ m_hist[w, 2] = denormal_filter(outx);
+ outBuffer[outIndex] = (WDL_ResampleSample)m_hist[w, 2];
+
+ outIndex += span;
+ }
+ }
+
+ double denormal_filter(float x)
+ {
+ // TODO: implement denormalisation
+ return x;
+ }
+ double denormal_filter(double x)
+ {
+ // TODO: implement denormalisation
+ return x;
+ }
+
+ private double m_fpos;
+ private double m_a1, m_a2;
+ private double m_b0, m_b1, m_b2;
+ private double[,] m_hist;
+ }
+
+ }
+}
diff --git a/NAudio/FileFormats/Map/CakewalkDrumMapping.cs b/NAudio/FileFormats/Map/CakewalkDrumMapping.cs
new file mode 100644
index 00000000..ae524fb7
--- /dev/null
+++ b/NAudio/FileFormats/Map/CakewalkDrumMapping.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace NAudio.FileFormats.Map
+{
+ ///
+ /// Represents an entry in a Cakewalk drum map
+ ///
+ public class CakewalkDrumMapping
+ {
+ ///
+ /// User customisable note name
+ ///
+ public string NoteName { get; set; }
+
+ ///
+ /// Input MIDI note number
+ ///
+ public int InNote { get; set; }
+
+ ///
+ /// Output MIDI note number
+ ///
+ public int OutNote { get; set; }
+
+ ///
+ /// Output port
+ ///
+ public int OutPort { get; set; }
+
+ ///
+ /// Output MIDI Channel
+ ///
+ public int Channel { get; set; }
+
+ ///
+ /// Velocity adjustment
+ ///
+ public int VelocityAdjust { get; set; }
+
+ ///
+ /// Velocity scaling - in percent
+ ///
+ public float VelocityScale { get; set; }
+
+ ///
+ /// Describes this drum map entry
+ ///
+ public override string ToString()
+ {
+ return String.Format("{0} In:{1} Out:{2} Ch:{3} Port:{4} Vel+:{5} Vel:{6}%",
+ NoteName, InNote, OutNote, Channel, OutPort, VelocityAdjust, VelocityScale*100);
+ }
+ }
+}
diff --git a/NAudio/FileFormats/Map/CakewalkMapFile.cs b/NAudio/FileFormats/Map/CakewalkMapFile.cs
new file mode 100644
index 00000000..1607a5c6
--- /dev/null
+++ b/NAudio/FileFormats/Map/CakewalkMapFile.cs
@@ -0,0 +1,167 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.FileFormats.Map
+{
+ ///
+ /// Represents a Cakewalk Drum Map file (.map)
+ ///
+ public class CakewalkMapFile
+ {
+ private int mapEntryCount;
+ private readonly List drumMappings;
+ private MapBlockHeader fileHeader1;
+ private MapBlockHeader fileHeader2;
+ private MapBlockHeader mapNameHeader;
+ private MapBlockHeader outputs1Header;
+ private MapBlockHeader outputs2Header;
+ private MapBlockHeader outputs3Header;
+ int outputs1Count;
+ int outputs2Count;
+ int outputs3Count;
+
+ private string mapName;
+
+ ///
+ /// Parses a Cakewalk Drum Map file
+ ///
+ /// Path of the .map file
+ public CakewalkMapFile(string filename)
+ {
+ using (var reader = new BinaryReader(File.OpenRead(filename),Encoding.Unicode))
+ {
+ drumMappings = new List();
+ ReadMapHeader(reader);
+ for (int n = 0; n < mapEntryCount; n++)
+ {
+ drumMappings.Add(ReadMapEntry(reader));
+ }
+ ReadMapName(reader);
+ ReadOutputsSection1(reader);
+ if (reader.BaseStream.Position == reader.BaseStream.Length)
+ return;
+ ReadOutputsSection2(reader);
+ if (reader.BaseStream.Position == reader.BaseStream.Length)
+ return;
+ ReadOutputsSection3(reader);
+ System.Diagnostics.Debug.Assert(reader.BaseStream.Position == reader.BaseStream.Length);
+ }
+ }
+
+ ///
+ /// The drum mappings in this drum map
+ ///
+ public List DrumMappings
+ {
+ get { return drumMappings; }
+ }
+
+
+ private void ReadMapHeader(BinaryReader reader)
+ {
+ fileHeader1 = MapBlockHeader.Read(reader);
+ fileHeader2 = MapBlockHeader.Read(reader);
+ mapEntryCount = reader.ReadInt32();
+ }
+
+ private CakewalkDrumMapping ReadMapEntry(BinaryReader reader)
+ {
+ var mapping = new CakewalkDrumMapping();
+ reader.ReadInt32(); // unknown
+ mapping.InNote = reader.ReadInt32();
+ reader.ReadInt32(); // unknown
+ reader.ReadInt32(); // unknown
+ reader.ReadInt32(); // unknown
+ reader.ReadInt32(); // unknown
+ reader.ReadInt32(); // unknown
+ reader.ReadInt32(); // unknown
+ mapping.VelocityScale = reader.ReadSingle();
+ mapping.Channel = reader.ReadInt32();
+ mapping.OutNote = reader.ReadInt32();
+ mapping.OutPort = reader.ReadInt32();
+ mapping.VelocityAdjust = reader.ReadInt32();
+ char[] name = reader.ReadChars(32);
+ int nameLength;
+ for (nameLength = 0; nameLength < name.Length; nameLength++)
+ {
+ if (name[nameLength] == 0)
+ break;
+ }
+ mapping.NoteName = new string(name, 0, nameLength);
+ return mapping;
+ }
+
+
+ private void ReadMapName(BinaryReader reader)
+ {
+ mapNameHeader = MapBlockHeader.Read(reader);
+ char[] name = reader.ReadChars(34);
+ int nameLength;
+ for (nameLength = 0; nameLength < name.Length; nameLength++)
+ {
+ if (name[nameLength] == 0)
+ break;
+ }
+ mapName = new string(name,0,nameLength);
+ reader.ReadBytes(98); // unknown
+ }
+
+ private void ReadOutputsSection1(BinaryReader reader)
+ {
+ outputs1Header = MapBlockHeader.Read(reader);
+ outputs1Count = reader.ReadInt32();
+ for (int n = 0; n < outputs1Count; n++)
+ {
+ reader.ReadBytes(20); // data
+ }
+ }
+
+ private void ReadOutputsSection2(BinaryReader reader)
+ {
+ outputs2Header = MapBlockHeader.Read(reader);
+ outputs2Count = reader.ReadInt32();
+ for (int n = 0; n < outputs2Count; n++)
+ {
+ reader.ReadBytes(24); // data
+ }
+ }
+
+ private void ReadOutputsSection3(BinaryReader reader)
+ {
+ outputs3Header = MapBlockHeader.Read(reader);
+ if (outputs3Header.Length > 0)
+ {
+ outputs3Count = reader.ReadInt32();
+ for (int n = 0; n < outputs3Count; n++)
+ {
+ reader.ReadBytes(36); // data
+ }
+ }
+ }
+
+ ///
+ /// Describes this drum map
+ ///
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.AppendFormat("FileHeader1: {0}\r\n", fileHeader1);
+ sb.AppendFormat("FileHeader2: {0}\r\n", fileHeader2);
+ sb.AppendFormat("MapEntryCount: {0}\r\n", mapEntryCount);
+ foreach (var mapping in drumMappings)
+ {
+ sb.AppendFormat(" Map: {0}\r\n", mapping);
+ }
+ sb.AppendFormat("MapNameHeader: {0}\r\n", mapNameHeader);
+ sb.AppendFormat("MapName: {0}\r\n", mapName);
+ sb.AppendFormat("Outputs1Header: {0} Count: {1}\r\n", outputs1Header, outputs1Count);
+ sb.AppendFormat("Outputs2Header: {0} Count: {1}\r\n", outputs2Header, outputs2Count);
+ sb.AppendFormat("Outputs3Header: {0} Count: {1}\r\n", outputs3Header, outputs3Count);
+ return sb.ToString();
+ }
+ }
+
+
+}
diff --git a/NAudio/FileFormats/Map/MapBlockHeader.cs b/NAudio/FileFormats/Map/MapBlockHeader.cs
new file mode 100644
index 00000000..17c6809a
--- /dev/null
+++ b/NAudio/FileFormats/Map/MapBlockHeader.cs
@@ -0,0 +1,34 @@
+using System;
+using System.IO;
+
+namespace NAudio.FileFormats.Map
+{
+ class MapBlockHeader
+ {
+ int length; // surely this is length
+ int value2;
+ short value3;
+ short value4;
+
+ public static MapBlockHeader Read(BinaryReader reader)
+ {
+ var header = new MapBlockHeader();
+ header.length = reader.ReadInt32(); // usually first 2 bytes have a value
+ header.value2 = reader.ReadInt32(); // usually 0
+ header.value3 = reader.ReadInt16(); // 0,1,2,3
+ header.value4 = reader.ReadInt16(); // 0x1017 (sometimes 0x1018
+ return header;
+ }
+
+ public override string ToString()
+ {
+ return String.Format("{0} {1:X8} {2:X4} {3:X4}",
+ length, value2, value3, value4);
+ }
+
+ public int Length
+ {
+ get { return length; }
+ }
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/ChannelMode.cs b/NAudio/FileFormats/Mp3/ChannelMode.cs
new file mode 100644
index 00000000..7fd80f9d
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/ChannelMode.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Channel Mode
+ ///
+ public enum ChannelMode
+ {
+ ///
+ /// Stereo
+ ///
+ Stereo,
+ ///
+ /// Joint Stereo
+ ///
+ JointStereo,
+ ///
+ /// Dual Channel
+ ///
+ DualChannel,
+ ///
+ /// Mono
+ ///
+ Mono
+ }
+
+}
diff --git a/NAudio/FileFormats/Mp3/DmoMp3FrameDecompressor.cs b/NAudio/FileFormats/Mp3/DmoMp3FrameDecompressor.cs
new file mode 100644
index 00000000..819e8bfe
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/DmoMp3FrameDecompressor.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Dmo;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudio.FileFormats.Mp3
+{
+ ///
+ /// MP3 Frame decompressor using the Windows Media MP3 Decoder DMO object
+ ///
+ public class DmoMp3FrameDecompressor : IMp3FrameDecompressor
+ {
+ private WindowsMediaMp3Decoder mp3Decoder;
+ private WaveFormat pcmFormat;
+ private MediaBuffer inputMediaBuffer;
+ private DmoOutputDataBuffer outputBuffer;
+ private bool reposition;
+
+ ///
+ /// Initializes a new instance of the DMO MP3 Frame decompressor
+ ///
+ ///
+ public DmoMp3FrameDecompressor(WaveFormat sourceFormat)
+ {
+ this.mp3Decoder = new WindowsMediaMp3Decoder();
+ if (!mp3Decoder.MediaObject.SupportsInputWaveFormat(0, sourceFormat))
+ {
+ throw new ArgumentException("Unsupported input format");
+ }
+ mp3Decoder.MediaObject.SetInputWaveFormat(0, sourceFormat);
+ pcmFormat = new WaveFormat(sourceFormat.SampleRate, sourceFormat.Channels); // 16 bit
+ if (!mp3Decoder.MediaObject.SupportsOutputWaveFormat(0, pcmFormat))
+ {
+ throw new ArgumentException(String.Format("Unsupported output format {0}", pcmFormat));
+ }
+ mp3Decoder.MediaObject.SetOutputWaveFormat(0, pcmFormat);
+
+ // a second is more than enough to decompress a frame at a time
+ inputMediaBuffer = new MediaBuffer(sourceFormat.AverageBytesPerSecond);
+ outputBuffer = new DmoOutputDataBuffer(pcmFormat.AverageBytesPerSecond);
+ }
+
+ ///
+ /// Converted PCM WaveFormat
+ ///
+ public WaveFormat OutputFormat { get { return pcmFormat; } }
+
+ ///
+ /// Decompress a single frame of MP3
+ ///
+ public int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset)
+ {
+ // 1. copy into our DMO's input buffer
+ inputMediaBuffer.LoadData(frame.RawData, frame.FrameLength);
+
+ if (reposition)
+ {
+ mp3Decoder.MediaObject.Flush();
+ reposition = false;
+ }
+
+ // 2. Give the input buffer to the DMO to process
+ mp3Decoder.MediaObject.ProcessInput(0, inputMediaBuffer, DmoInputDataBufferFlags.None, 0, 0);
+
+ outputBuffer.MediaBuffer.SetLength(0);
+ outputBuffer.StatusFlags = DmoOutputDataBufferFlags.None;
+
+ // 3. Now ask the DMO for some output data
+ mp3Decoder.MediaObject.ProcessOutput(DmoProcessOutputFlags.None, 1, new[] { outputBuffer });
+
+ if (outputBuffer.Length == 0)
+ {
+ Debug.WriteLine("ResamplerDmoStream.Read: No output data available");
+ return 0;
+ }
+
+ // 5. Now get the data out of the output buffer
+ outputBuffer.RetrieveData(dest, destOffset);
+ Debug.Assert(!outputBuffer.MoreDataAvailable, "have not implemented more data available yet");
+
+ return outputBuffer.Length;
+ }
+
+ ///
+ /// Alerts us that a reposition has occured so the MP3 decoder needs to reset its state
+ ///
+ public void Reset()
+ {
+ reposition = true;
+ }
+
+ ///
+ /// Dispose of this obejct and clean up resources
+ ///
+ public void Dispose()
+ {
+ if (inputMediaBuffer != null)
+ {
+ inputMediaBuffer.Dispose();
+ inputMediaBuffer = null;
+ }
+ outputBuffer.Dispose();
+ if (mp3Decoder!= null)
+ {
+ mp3Decoder.Dispose();
+ mp3Decoder = null;
+ }
+ }
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/IMp3FrameDecompressor.cs b/NAudio/FileFormats/Mp3/IMp3FrameDecompressor.cs
new file mode 100644
index 00000000..cd2716cd
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/IMp3FrameDecompressor.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Interface for MP3 frame by frame decoder
+ ///
+ public interface IMp3FrameDecompressor : IDisposable
+ {
+ ///
+ /// Decompress a single MP3 frame
+ ///
+ /// Frame to decompress
+ /// Output buffer
+ /// Offset within output buffer
+ /// Bytes written to output buffer
+ int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset);
+
+ ///
+ /// Tell the decoder that we have repositioned
+ ///
+ void Reset();
+
+ ///
+ /// PCM format that we are converting into
+ ///
+ WaveFormat OutputFormat { get; }
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/Id3v2Tag.cs b/NAudio/FileFormats/Mp3/Id3v2Tag.cs
new file mode 100644
index 00000000..c06e2ac0
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/Id3v2Tag.cs
@@ -0,0 +1,237 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// An ID3v2 Tag
+ ///
+ public class Id3v2Tag
+ {
+ private long tagStartPosition;
+ private long tagEndPosition;
+ private byte[] rawData;
+
+ ///
+ /// Reads an ID3v2 tag from a stream
+ ///
+ public static Id3v2Tag ReadTag(Stream input)
+ {
+ try
+ {
+ return new Id3v2Tag(input);
+ }
+ catch (FormatException)
+ {
+ return null;
+ }
+ }
+
+ #region Id3v2 Creation from key-value pairs
+
+ ///
+ /// Creates a new ID3v2 tag from a collection of key-value pairs.
+ ///
+ /// A collection of key-value pairs containing the tags to include in the ID3v2 tag.
+ /// A new ID3v2 tag
+ public static Id3v2Tag Create(IEnumerable> tags)
+ {
+ return Id3v2Tag.ReadTag(CreateId3v2TagStream(tags));
+ }
+
+ ///
+ /// Convert the frame size to a byte array.
+ ///
+ /// The frame body size.
+ ///
+ static byte[] FrameSizeToBytes(int n)
+ {
+ byte[] result = BitConverter.GetBytes(n);
+ Array.Reverse(result);
+ return result;
+ }
+
+ ///
+ /// Creates an ID3v2 frame for the given key-value pair.
+ ///
+ ///
+ ///
+ ///
+ static byte[] CreateId3v2Frame(string key, string value)
+ {
+ if (string.IsNullOrEmpty(key))
+ {
+ throw new ArgumentNullException("key");
+ }
+
+ if (string.IsNullOrEmpty(value))
+ {
+ throw new ArgumentNullException("value");
+ }
+
+ if (key.Length != 4)
+ {
+ throw new ArgumentOutOfRangeException("key", "key " + key + " must be 4 characters long");
+ }
+
+ const byte UnicodeEncoding = 01; // encode text in Unicode
+ byte[] UnicodeOrder = new byte[] { 0xff, 0xfe }; // Unicode byte order mark
+ byte[] language = new byte[] { 0, 0, 0 }; // language is empty (only used in COMM -> comment)
+ byte[] shortDescription = new byte[] { 0, 0 }; // short description is empty (only used in COMM -> comment)
+
+ byte[] body;
+ if (key == "COMM") // comment
+ {
+ body = ByteArrayExtensions.Concat(
+ new byte[] { UnicodeEncoding },
+ language,
+ shortDescription,
+ UnicodeOrder,
+ Encoding.Unicode.GetBytes(value));
+ }
+ else
+ {
+ body = ByteArrayExtensions.Concat(
+ new byte[] { UnicodeEncoding },
+ UnicodeOrder,
+ Encoding.Unicode.GetBytes(value));
+ }
+
+ return ByteArrayExtensions.Concat(
+ // needs review - have converted to UTF8 as Win 8 has no Encoding.ASCII,
+ // need to check what the rules are for ID3v2 tag identifiers
+ Encoding.UTF8.GetBytes(key),
+ FrameSizeToBytes(body.Length),
+ new byte[] { 0, 0 }, // flags
+ body);
+ }
+
+ ///
+ /// Gets the Id3v2 Header size. The size is encoded so that only 7 bits per byte are actually used.
+ ///
+ ///
+ ///
+ static byte[] GetId3TagHeaderSize(int size)
+ {
+ byte[] result = new byte[4];
+ for (int idx = result.Length - 1; idx >= 0; idx--)
+ {
+ result[idx] = (byte)(size % 128);
+ size = size / 128;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Creates the Id3v2 tag header and returns is as a byte array.
+ ///
+ /// The Id3v2 frames that will be included in the file. This is used to calculate the ID3v2 tag size.
+ ///
+ static byte[] CreateId3v2TagHeader(IEnumerable frames)
+ {
+ int size = 0;
+ foreach (byte[] frame in frames)
+ {
+ size += frame.Length;
+ }
+
+ byte[] tagHeader = ByteArrayExtensions.Concat(
+ Encoding.UTF8.GetBytes("ID3"),
+ new byte[] { 3, 0 }, // version
+ new byte[] { 0 }, // flags
+ GetId3TagHeaderSize(size));
+ return tagHeader;
+ }
+
+ ///
+ /// Creates the Id3v2 tag for the given key-value pairs and returns it in the a stream.
+ ///
+ ///
+ ///
+ static Stream CreateId3v2TagStream(IEnumerable> tags)
+ {
+ List frames = new List();
+ foreach (KeyValuePair tag in tags)
+ {
+ frames.Add(CreateId3v2Frame(tag.Key, tag.Value));
+ }
+
+ byte[] header = CreateId3v2TagHeader(frames);
+
+ MemoryStream ms = new MemoryStream();
+ ms.Write(header, 0, header.Length);
+ foreach (byte[] frame in frames)
+ {
+ ms.Write(frame, 0, frame.Length);
+ }
+
+ ms.Position = 0;
+ return ms;
+ }
+
+ #endregion
+
+ private Id3v2Tag(Stream input)
+ {
+ tagStartPosition = input.Position;
+ BinaryReader reader = new BinaryReader(input);
+ byte[] headerBytes = reader.ReadBytes(10);
+ if ((headerBytes[0] == (byte)'I') &&
+ (headerBytes[1] == (byte)'D') &&
+ (headerBytes[2] == '3'))
+ {
+
+ // http://www.id3.org/develop.html
+ // OK found an ID3 tag
+ // bytes 3 & 4 are ID3v2 version
+
+ if ((headerBytes[5] & 0x40) == 0x40)
+ {
+ // extended header present
+ byte[] extendedHeader = reader.ReadBytes(4);
+ int extendedHeaderLength = extendedHeader[0] * (1 << 21);
+ extendedHeaderLength += extendedHeader[1] * (1 << 14);
+ extendedHeaderLength += extendedHeader[2] * (1 << 7);
+ extendedHeaderLength += extendedHeader[3];
+ }
+
+ // synchsafe
+ int dataLength = headerBytes[6] * (1 << 21);
+ dataLength += headerBytes[7] * (1 << 14);
+ dataLength += headerBytes[8] * (1 << 7);
+ dataLength += headerBytes[9];
+ byte[] tagData = reader.ReadBytes(dataLength);
+
+ if ((headerBytes[5] & 0x10) == 0x10)
+ {
+ // footer present
+ byte[] footer = reader.ReadBytes(10);
+ }
+ }
+ else
+ {
+ input.Position -= 10;
+ throw new FormatException("Not an ID3v2 tag");
+ }
+ tagEndPosition = input.Position;
+ input.Position = tagStartPosition;
+ rawData = reader.ReadBytes((int)(tagEndPosition - tagStartPosition));
+
+ }
+
+ ///
+ /// Raw data from this tag
+ ///
+ public byte[] RawData
+ {
+ get
+ {
+ return rawData;
+ }
+ }
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/Mp3Frame.cs b/NAudio/FileFormats/Mp3/Mp3Frame.cs
new file mode 100644
index 00000000..f2d47390
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/Mp3Frame.cs
@@ -0,0 +1,280 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.Wave
+{
+
+ ///
+ /// Represents an MP3 Frame
+ ///
+ public class Mp3Frame
+ {
+ private static readonly int[,,] bitRates = new int[,,] {
+ {
+ // MPEG Version 1
+ { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448 }, // Layer 1
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384 }, // Layer 2
+ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }, // Layer 3
+ },
+ {
+ // MPEG Version 2 & 2.5
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256 }, // Layer 1
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, // Layer 2
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 }, // Layer 3 (same as layer 2)
+ }
+ };
+ private static readonly int[,] samplesPerFrame = new int[,] {
+ { // MPEG Version 1
+ 384, // Layer1
+ 1152, // Layer2
+ 1152 // Layer3
+ },
+ { // MPEG Version 2 & 2.5
+ 384, // Layer1
+ 1152, // Layer2
+ 576 // Layer3
+ }
+ };
+
+ private static readonly int[] sampleRatesVersion1 = new int[] {44100, 48000, 32000};
+ private static readonly int[] sampleRatesVersion2 = new int[] {22050, 24000, 16000};
+ private static readonly int[] sampleRatesVersion25 = new int[] {11025, 12000, 8000};
+
+ //private short crc;
+ private const int MaxFrameLength = 16*1024;
+
+ ///
+ /// Reads an MP3 frame from a stream
+ ///
+ /// input stream
+ /// A valid MP3 frame, or null if none found
+ public static Mp3Frame LoadFromStream(Stream input)
+ {
+ return LoadFromStream(input, true);
+ }
+
+ /// Reads an MP3Frame from a stream
+ /// http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm has some good info
+ /// also see http://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx
+ ///
+ /// A valid MP3 frame, or null if none found
+ public static Mp3Frame LoadFromStream(Stream input, bool readData)
+ {
+ var frame = new Mp3Frame();
+ frame.FileOffset = input.Position;
+ byte[] headerBytes = new byte[4];
+ int bytesRead = input.Read(headerBytes, 0, headerBytes.Length);
+ if (bytesRead < headerBytes.Length)
+ {
+ // reached end of stream, no more MP3 frames
+ return null;
+ }
+ while (!IsValidHeader(headerBytes, frame))
+ {
+ // shift down by one and try again
+ headerBytes[0] = headerBytes[1];
+ headerBytes[1] = headerBytes[2];
+ headerBytes[2] = headerBytes[3];
+ bytesRead = input.Read(headerBytes, 3, 1);
+ if (bytesRead < 1)
+ {
+ return null;
+ }
+ frame.FileOffset++;
+ }
+ /* no longer read the CRC since we include this in framelengthbytes
+ if (this.crcPresent)
+ this.crc = reader.ReadInt16();*/
+
+ int bytesRequired = frame.FrameLength - 4;
+ if (readData)
+ {
+ frame.RawData = new byte[frame.FrameLength];
+ Array.Copy(headerBytes, frame.RawData, 4);
+ bytesRead = input.Read(frame.RawData, 4, bytesRequired);
+ if (bytesRead < bytesRequired)
+ {
+ // TODO: could have an option to suppress this, although it does indicate a corrupt file
+ // for now, caller should handle this exception
+ throw new EndOfStreamException("Unexpected end of stream before frame complete");
+ }
+ }
+ else
+ {
+ // n.b. readData should not be false if input stream does not support seeking
+ input.Position += bytesRequired;
+ }
+
+ return frame;
+ }
+
+
+ ///
+ /// Constructs an MP3 frame
+ ///
+ private Mp3Frame()
+ {
+
+ }
+
+ ///
+ /// checks if the four bytes represent a valid header,
+ /// if they are, will parse the values into Mp3Frame
+ ///
+ private static bool IsValidHeader(byte[] headerBytes, Mp3Frame frame)
+ {
+ if ((headerBytes[0] == 0xFF) && ((headerBytes[1] & 0xE0) == 0xE0))
+ {
+ // TODO: could do with a bitstream class here
+ frame.MpegVersion = (MpegVersion) ((headerBytes[1] & 0x18) >> 3);
+ if (frame.MpegVersion == MpegVersion.Reserved)
+ {
+ //throw new FormatException("Unsupported MPEG Version");
+ return false;
+ }
+
+ frame.MpegLayer = (MpegLayer) ((headerBytes[1] & 0x06) >> 1);
+
+ if (frame.MpegLayer == MpegLayer.Reserved)
+ {
+ return false;
+ }
+ int layerIndex = frame.MpegLayer == MpegLayer.Layer1 ? 0 : frame.MpegLayer == MpegLayer.Layer2 ? 1 : 2;
+ frame.CrcPresent = (headerBytes[1] & 0x01) == 0x00;
+ frame.BitRateIndex = (headerBytes[2] & 0xF0) >> 4;
+ if (frame.BitRateIndex == 15)
+ {
+ // invalid index
+ return false;
+ }
+ int versionIndex = frame.MpegVersion == Wave.MpegVersion.Version1 ? 0 : 1;
+ frame.BitRate = bitRates[versionIndex, layerIndex, frame.BitRateIndex]*1000;
+ if (frame.BitRate == 0)
+ {
+ return false;
+ }
+ int sampleFrequencyIndex = (headerBytes[2] & 0x0C) >> 2;
+ if (sampleFrequencyIndex == 3)
+ {
+ return false;
+ }
+
+ if (frame.MpegVersion == MpegVersion.Version1)
+ {
+ frame.SampleRate = sampleRatesVersion1[sampleFrequencyIndex];
+ }
+ else if (frame.MpegVersion == MpegVersion.Version2)
+ {
+ frame.SampleRate = sampleRatesVersion2[sampleFrequencyIndex];
+ }
+ else
+ {
+ // mpegVersion == MpegVersion.Version25
+ frame.SampleRate = sampleRatesVersion25[sampleFrequencyIndex];
+ }
+
+ bool padding = (headerBytes[2] & 0x02) == 0x02;
+ bool privateBit = (headerBytes[2] & 0x01) == 0x01;
+ frame.ChannelMode = (ChannelMode) ((headerBytes[3] & 0xC0) >> 6);
+ frame.ChannelExtension = (headerBytes[3] & 0x30) >> 4;
+ if (frame.ChannelExtension != 0 && frame.ChannelMode != ChannelMode.JointStereo)
+ {
+ return false;
+ }
+
+
+ frame.Copyright = (headerBytes[3] & 0x08) == 0x08;
+ bool original = (headerBytes[3] & 0x04) == 0x04;
+ int emphasis = (headerBytes[3] & 0x03);
+
+ int nPadding = padding ? 1 : 0;
+
+ frame.SampleCount = samplesPerFrame[versionIndex, layerIndex];
+ int coefficient = frame.SampleCount/8;
+ if (frame.MpegLayer == MpegLayer.Layer1)
+ {
+ frame.FrameLength = (coefficient*frame.BitRate/frame.SampleRate + nPadding)*4;
+ }
+ else
+ {
+ frame.FrameLength = (coefficient*frame.BitRate)/frame.SampleRate + nPadding;
+ }
+
+ if (frame.FrameLength > MaxFrameLength)
+ {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Sample rate of this frame
+ ///
+ public int SampleRate { get; private set; }
+
+ ///
+ /// Frame length in bytes
+ ///
+ public int FrameLength { get; private set; }
+
+ ///
+ /// Bit Rate
+ ///
+ public int BitRate { get; private set; }
+
+ ///
+ /// Raw frame data (includes header bytes)
+ ///
+ public byte[] RawData { get; private set; }
+
+ ///
+ /// MPEG Version
+ ///
+ public MpegVersion MpegVersion { get; private set; }
+
+ ///
+ /// MPEG Layer
+ ///
+ public MpegLayer MpegLayer { get; private set; }
+
+ ///
+ /// Channel Mode
+ ///
+ public ChannelMode ChannelMode { get; private set; }
+
+ ///
+ /// The number of samples in this frame
+ ///
+ public int SampleCount { get; private set; }
+
+ ///
+ /// The channel extension bits
+ ///
+ public int ChannelExtension { get; private set; }
+
+ ///
+ /// The bitrate index (directly from the header)
+ ///
+ public int BitRateIndex { get; private set; }
+
+ ///
+ /// Whether the Copyright bit is set
+ ///
+ public bool Copyright { get; private set; }
+
+ ///
+ /// Whether a CRC is present
+ ///
+ public bool CrcPresent { get; private set; }
+
+
+ ///
+ /// Not part of the MP3 frame itself - indicates where in the stream we found this header
+ ///
+ public long FileOffset { get; private set; }
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/Mp3FrameDecompressor.cs b/NAudio/FileFormats/Mp3/Mp3FrameDecompressor.cs
new file mode 100644
index 00000000..5d94faf0
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/Mp3FrameDecompressor.cs
@@ -0,0 +1,95 @@
+using System;
+using NAudio.Wave.Compression;
+
+namespace NAudio.Wave
+{
+ ///
+ /// MP3 Frame Decompressor using ACM
+ ///
+ public class AcmMp3FrameDecompressor : IMp3FrameDecompressor
+ {
+ private readonly AcmStream conversionStream;
+ private readonly WaveFormat pcmFormat;
+ private bool disposed;
+
+ ///
+ /// Creates a new ACM frame decompressor
+ ///
+ /// The MP3 source format
+ public AcmMp3FrameDecompressor(WaveFormat sourceFormat)
+ {
+ this.pcmFormat = AcmStream.SuggestPcmFormat(sourceFormat);
+ try
+ {
+ conversionStream = new AcmStream(sourceFormat, pcmFormat);
+ }
+ catch (Exception)
+ {
+ disposed = true;
+ GC.SuppressFinalize(this);
+ throw;
+ }
+ }
+
+ ///
+ /// Output format (PCM)
+ ///
+ public WaveFormat OutputFormat { get { return pcmFormat; } }
+
+ ///
+ /// Decompresses a frame
+ ///
+ /// The MP3 frame
+ /// destination buffer
+ /// Offset within destination buffer
+ /// Bytes written into destination buffer
+ public int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset)
+ {
+ if (frame == null)
+ {
+ throw new ArgumentNullException("frame", "You must provide a non-null Mp3Frame to decompress");
+ }
+ Array.Copy(frame.RawData, conversionStream.SourceBuffer, frame.FrameLength);
+ int sourceBytesConverted = 0;
+ int converted = conversionStream.Convert(frame.FrameLength, out sourceBytesConverted);
+ if (sourceBytesConverted != frame.FrameLength)
+ {
+ throw new InvalidOperationException(String.Format("Couldn't convert the whole MP3 frame (converted {0}/{1})",
+ sourceBytesConverted, frame.FrameLength));
+ }
+ Array.Copy(conversionStream.DestBuffer, 0, dest, destOffset, converted);
+ return converted;
+ }
+
+ ///
+ /// Resets the MP3 Frame Decompressor after a reposition operation
+ ///
+ public void Reset()
+ {
+ conversionStream.Reposition();
+ }
+
+ ///
+ /// Disposes of this MP3 frame decompressor
+ ///
+ public void Dispose()
+ {
+ if (!disposed)
+ {
+ disposed = true;
+ if(conversionStream != null)
+ conversionStream.Dispose();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ ///
+ /// Finalizer ensuring that resources get released properly
+ ///
+ ~AcmMp3FrameDecompressor()
+ {
+ System.Diagnostics.Debug.Assert(false, "AcmMp3FrameDecompressor Dispose was not called");
+ Dispose();
+ }
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/MpegLayer.cs b/NAudio/FileFormats/Mp3/MpegLayer.cs
new file mode 100644
index 00000000..1fe711dc
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/MpegLayer.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// MPEG Layer flags
+ ///
+ public enum MpegLayer
+ {
+ ///
+ /// Reserved
+ ///
+ Reserved,
+ ///
+ /// Layer 3
+ ///
+ Layer3,
+ ///
+ /// Layer 2
+ ///
+ Layer2,
+ ///
+ /// Layer 1
+ ///
+ Layer1
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/MpegVersion.cs b/NAudio/FileFormats/Mp3/MpegVersion.cs
new file mode 100644
index 00000000..36d58e33
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/MpegVersion.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// MPEG Version Flags
+ ///
+ public enum MpegVersion
+ {
+ ///
+ /// Version 2.5
+ ///
+ Version25,
+ ///
+ /// Reserved
+ ///
+ Reserved,
+ ///
+ /// Version 2
+ ///
+ Version2,
+ ///
+ /// Version 1
+ ///
+ Version1
+ }
+}
diff --git a/NAudio/FileFormats/Mp3/XingHeader.cs b/NAudio/FileFormats/Mp3/XingHeader.cs
new file mode 100644
index 00000000..6027bd2b
--- /dev/null
+++ b/NAudio/FileFormats/Mp3/XingHeader.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Represents a Xing VBR header
+ ///
+ public class XingHeader
+ {
+ [Flags]
+ enum XingHeaderOptions
+ {
+ Frames = 1,
+ Bytes = 2,
+ Toc = 4,
+ VbrScale = 8
+ }
+
+ private static int[] sr_table = { 44100, 48000, 32000, 99999 };
+ private int vbrScale = -1;
+ private int startOffset;
+ private int endOffset;
+
+ private int tocOffset = -1;
+ private int framesOffset = -1;
+ private int bytesOffset = -1;
+ private Mp3Frame frame;
+
+ private static int ReadBigEndian(byte[] buffer, int offset)
+ {
+ int x;
+ // big endian extract
+ x = buffer[offset+0];
+ x <<= 8;
+ x |= buffer[offset+1];
+ x <<= 8;
+ x |= buffer[offset+2];
+ x <<= 8;
+ x |= buffer[offset+3];
+
+ return x;
+ }
+
+ private void WriteBigEndian(byte[] buffer, int offset, int value)
+ {
+ byte[] littleEndian = BitConverter.GetBytes(value);
+ for (int n = 0; n < 4; n++)
+ {
+ buffer[offset + 4 - n] = littleEndian[n];
+ }
+ }
+
+ ///
+ /// Load Xing Header
+ ///
+ /// Frame
+ /// Xing Header
+ public static XingHeader LoadXingHeader(Mp3Frame frame)
+ {
+ XingHeader xingHeader = new XingHeader();
+ xingHeader.frame = frame;
+ int offset = 0;
+
+ if (frame.MpegVersion == MpegVersion.Version1)
+ {
+ if (frame.ChannelMode != ChannelMode.Mono)
+ offset = 32 + 4;
+ else
+ offset = 17 + 4;
+ }
+ else if (frame.MpegVersion == MpegVersion.Version2)
+ {
+ if (frame.ChannelMode != ChannelMode.Mono)
+ offset = 17 + 4;
+ else
+ offset = 9 + 4;
+ }
+ else
+ {
+ return null;
+ // throw new FormatException("Unsupported MPEG Version");
+ }
+
+ if ((frame.RawData[offset + 0] == 'X') &&
+ (frame.RawData[offset + 1] == 'i') &&
+ (frame.RawData[offset + 2] == 'n') &&
+ (frame.RawData[offset + 3] == 'g'))
+ {
+ xingHeader.startOffset = offset;
+ offset += 4;
+ }
+ else
+ {
+ return null;
+ }
+
+ XingHeaderOptions flags = (XingHeaderOptions)ReadBigEndian(frame.RawData, offset);
+ offset += 4;
+
+ if ((flags & XingHeaderOptions.Frames) != 0)
+ {
+ xingHeader.framesOffset = offset;
+ offset += 4;
+ }
+ if ((flags & XingHeaderOptions.Bytes) != 0)
+ {
+ xingHeader.bytesOffset = offset;
+ offset += 4;
+ }
+ if ((flags & XingHeaderOptions.Toc) != 0)
+ {
+ xingHeader.tocOffset = offset;
+ offset += 100;
+ }
+ if ((flags & XingHeaderOptions.VbrScale) != 0)
+ {
+ xingHeader.vbrScale = ReadBigEndian(frame.RawData, offset);
+ offset += 4;
+ }
+ xingHeader.endOffset = offset;
+ return xingHeader;
+ }
+
+ ///
+ /// Sees if a frame contains a Xing header
+ ///
+ private XingHeader()
+ {
+ }
+
+ ///
+ /// Number of frames
+ ///
+ public int Frames
+ {
+ get
+ {
+ if(framesOffset == -1)
+ return -1;
+ return ReadBigEndian(frame.RawData, framesOffset);
+ }
+ set
+ {
+ if (framesOffset == -1)
+ throw new InvalidOperationException("Frames flag is not set");
+ WriteBigEndian(frame.RawData, framesOffset, value);
+ }
+ }
+
+ ///
+ /// Number of bytes
+ ///
+ public int Bytes
+ {
+ get
+ {
+ if(bytesOffset == -1)
+ return -1;
+ return ReadBigEndian(frame.RawData, bytesOffset);
+ }
+ set
+ {
+ if (framesOffset == -1)
+ throw new InvalidOperationException("Bytes flag is not set");
+ WriteBigEndian(frame.RawData, bytesOffset, value);
+ }
+ }
+
+ ///
+ /// VBR Scale property
+ ///
+ public int VbrScale
+ {
+ get { return vbrScale; }
+ }
+
+ ///
+ /// The MP3 frame
+ ///
+ public Mp3Frame Mp3Frame
+ {
+ get { return frame; }
+ }
+
+ }
+}
diff --git a/NAudio/FileFormats/Sfz/SfzFileReader.cs b/NAudio/FileFormats/Sfz/SfzFileReader.cs
new file mode 100644
index 00000000..beda4dd6
--- /dev/null
+++ b/NAudio/FileFormats/Sfz/SfzFileReader.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.Sfz
+{
+ enum SfzParseState
+ {
+ Initial,
+ Region,
+ }
+
+ class Group
+ {
+ }
+
+ class Region
+ {
+ }
+
+ class SfzFileReader
+ {
+ public SfzFileReader(string fileName)
+ {
+ StringBuilder trimmed = new StringBuilder();
+ using (StreamReader reader = new StreamReader(fileName))
+ {
+ //SfzParseState parseState = SfzParseState.Initial;
+ string line;
+ List regions = new List();
+ Group currentGroup = null;
+ Region currentRegion = null;
+ StringBuilder currentOpcode = new StringBuilder();
+ StringBuilder currentValue = new StringBuilder();
+ int lastSpace;
+ int lineNumber = 0;
+ while ((line = reader.ReadLine()) != null)
+ {
+ lineNumber++;
+ // 1. Strip comments
+ int commentIndex = line.IndexOf('/');
+ if(commentIndex != -1)
+ line = line.Substring(commentIndex);
+
+
+
+
+ for (int n = 0; n < line.Length; n++)
+ {
+
+ char c = line[n];
+ if(Char.IsWhiteSpace(c))
+ {
+ if (currentOpcode.Length == 0)
+ {
+ continue;
+ }
+ else if (currentValue.Length == 0)
+ {
+ throw new FormatException(String.Format("Invalid Whitespace Line {0}, Char {1}", lineNumber, n));
+ }
+ else
+ {
+ lastSpace = n;
+ currentValue.Append(c);
+ }
+ }
+ else if (c == '=')
+ {
+ }
+ else if (c == '<')
+ {
+ if (line.Substring(n).StartsWith(""))
+ {
+ if (currentRegion != null)
+ regions.Add(currentRegion);
+ currentRegion = new Region(); //currentGroup);
+ currentOpcode.Length = 0;
+ currentValue.Length = 0;
+ n += 7;
+ }
+ else if (line.Substring(n).StartsWith(""))
+ {
+ if (currentRegion != null)
+ regions.Add(currentRegion);
+ currentOpcode.Length = 0;
+ currentValue.Length = 0;
+ currentRegion = null;
+ currentGroup = new Group();
+ n += 6;
+ }
+ else
+ {
+ throw new FormatException(String.Format("Unrecognised section Line {0}, Char {1}", lineNumber, n));
+ }
+ }
+ else
+ {
+ //if(current.Length
+ }
+
+
+ }
+ }
+
+ }
+
+ }
+ }
+}
diff --git a/NAudio/FileFormats/SoundFont/Generator.cs b/NAudio/FileFormats/SoundFont/Generator.cs
new file mode 100644
index 00000000..ae1dd686
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/Generator.cs
@@ -0,0 +1,136 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Soundfont generator
+ ///
+ public class Generator
+ {
+ private GeneratorEnum generatorType;
+ private ushort rawAmount;
+ private Instrument instrument;
+ private SampleHeader sampleHeader;
+
+ ///
+ /// Gets the generator type
+ ///
+ public GeneratorEnum GeneratorType
+ {
+ get
+ {
+ return generatorType;
+ }
+ set
+ {
+ generatorType = value;
+ }
+ }
+
+ ///
+ /// Generator amount as an unsigned short
+ ///
+ public ushort UInt16Amount
+ {
+ get
+ {
+ return rawAmount;
+ }
+ set
+ {
+ rawAmount = value;
+ }
+ }
+
+ ///
+ /// Generator amount as a signed short
+ ///
+ public short Int16Amount
+ {
+ get
+ {
+ return (short) rawAmount;
+ }
+ set
+ {
+ rawAmount = (ushort) value;
+ }
+ }
+
+ ///
+ /// Low byte amount
+ ///
+ public byte LowByteAmount
+ {
+ get
+ {
+ return (byte) (rawAmount & 0x00FF);
+ }
+ set
+ {
+ rawAmount &= 0xFF00;
+ rawAmount += value;
+ }
+ }
+
+ ///
+ /// High byte amount
+ ///
+ public byte HighByteAmount
+ {
+ get
+ {
+ return (byte) ((rawAmount & 0xFF00) >> 8);
+ }
+ set
+ {
+ rawAmount &= 0x00FF;
+ rawAmount += (ushort) (value << 8);
+ }
+ }
+
+ ///
+ /// Instrument
+ ///
+ public Instrument Instrument
+ {
+ get
+ {
+ return instrument;
+ }
+ set
+ {
+ instrument = value;
+ }
+ }
+
+ ///
+ /// Sample Header
+ ///
+ public SampleHeader SampleHeader
+ {
+ get
+ {
+ return sampleHeader;
+ }
+ set
+ {
+ sampleHeader = value;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ if(generatorType == GeneratorEnum.Instrument)
+ return String.Format("Generator Instrument {0}",instrument.Name);
+ else if(generatorType == GeneratorEnum.SampleID)
+ return String.Format("Generator SampleID {0}",sampleHeader);
+ else
+ return String.Format("Generator {0} {1}",generatorType,rawAmount);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/GeneratorBuilder.cs b/NAudio/FileFormats/SoundFont/GeneratorBuilder.cs
new file mode 100644
index 00000000..e3adfa84
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/GeneratorBuilder.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont
+{
+ internal class GeneratorBuilder : StructureBuilder
+ {
+ public override Generator Read(BinaryReader br)
+ {
+ Generator g = new Generator();
+ g.GeneratorType = (GeneratorEnum) br.ReadUInt16();
+ g.UInt16Amount = br.ReadUInt16();
+ data.Add(g);
+ return g;
+ }
+
+ public override void Write(BinaryWriter bw, Generator o)
+ {
+ //Zone z = (Zone) o;
+ //bw.Write(p.---);
+ }
+
+ public override int Length {
+ get {
+ return 4;
+ }
+ }
+
+ public Generator[] Generators
+ {
+ get
+ {
+ return data.ToArray();
+ }
+ }
+
+ public void Load(Instrument[] instruments)
+ {
+ foreach(Generator g in Generators)
+ {
+ if(g.GeneratorType == GeneratorEnum.Instrument)
+ {
+ g.Instrument = instruments[g.UInt16Amount];
+ }
+ }
+ }
+
+ public void Load(SampleHeader[] sampleHeaders)
+ {
+ foreach(Generator g in Generators)
+ {
+ if(g.GeneratorType == GeneratorEnum.SampleID)
+ {
+ g.SampleHeader = sampleHeaders[g.UInt16Amount];
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/GeneratorEnum.cs b/NAudio/FileFormats/SoundFont/GeneratorEnum.cs
new file mode 100644
index 00000000..59b2c370
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/GeneratorEnum.cs
@@ -0,0 +1,133 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Generator types
+ ///
+ public enum GeneratorEnum
+ {
+ /// Start address offset
+ StartAddressOffset = 0,
+ /// End address offset
+ EndAddressOffset,
+ /// Start loop address offset
+ StartLoopAddressOffset,
+ /// End loop address offset
+ EndLoopAddressOffset,
+ /// Start address coarse offset
+ StartAddressCoarseOffset,
+ /// Modulation LFO to pitch
+ ModulationLFOToPitch,
+ /// Vibrato LFO to pitch
+ VibratoLFOToPitch,
+ /// Modulation envelope to pitch
+ ModulationEnvelopeToPitch,
+ /// Initial filter cutoff frequency
+ InitialFilterCutoffFrequency,
+ /// Initial filter Q
+ InitialFilterQ,
+ /// Modulation LFO to filter Cutoff frequency
+ ModulationLFOToFilterCutoffFrequency,
+ /// Modulation envelope to filter cutoff frequency
+ ModulationEnvelopeToFilterCutoffFrequency,
+ /// End address coarse offset
+ EndAddressCoarseOffset,
+ /// Modulation LFO to volume
+ ModulationLFOToVolume,
+ /// Unused
+ Unused1,
+ /// Chorus effects send
+ ChorusEffectsSend,
+ /// Reverb effects send
+ ReverbEffectsSend,
+ /// Pan
+ Pan,
+ /// Unused
+ Unused2,
+ /// Unused
+ Unused3,
+ /// Unused
+ Unused4,
+ /// Delay modulation LFO
+ DelayModulationLFO,
+ /// Frequency modulation LFO
+ FrequencyModulationLFO,
+ /// Delay vibrato LFO
+ DelayVibratoLFO,
+ /// Frequency vibrato LFO
+ FrequencyVibratoLFO,
+ /// Delay modulation envelope
+ DelayModulationEnvelope,
+ /// Attack modulation envelope
+ AttackModulationEnvelope,
+ /// Hold modulation envelope
+ HoldModulationEnvelope,
+ /// Decay modulation envelope
+ DecayModulationEnvelope,
+ /// Sustain modulation envelop
+ SustainModulationEnvelope,
+ /// Release modulation envelope
+ ReleaseModulationEnvelope,
+ /// Key number to modulation envelope hold
+ KeyNumberToModulationEnvelopeHold,
+ /// Key number to modulation envelope decay
+ KeyNumberToModulationEnvelopeDecay,
+ /// Delay volume envelope
+ DelayVolumeEnvelope,
+ /// Attack volume envelope
+ AttackVolumeEnvelope,
+ /// Hold volume envelope
+ HoldVolumeEnvelope,
+ /// Decay volume envelope
+ DecayVolumeEnvelope,
+ /// Sustain volume envelope
+ SustainVolumeEnvelope,
+ /// Release volume envelope
+ ReleaseVolumeEnvelope,
+ /// Key number to volume envelope hold
+ KeyNumberToVolumeEnvelopeHold,
+ /// Key number to volume envelope decay
+ KeyNumberToVolumeEnvelopeDecay,
+ /// Instrument
+ Instrument,
+ /// Reserved
+ Reserved1,
+ /// Key range
+ KeyRange,
+ /// Velocity range
+ VelocityRange,
+ /// Start loop address coarse offset
+ StartLoopAddressCoarseOffset,
+ /// Key number
+ KeyNumber,
+ /// Velocity
+ Velocity,
+ /// Initial attenuation
+ InitialAttenuation,
+ /// Reserved
+ Reserved2,
+ /// End loop address coarse offset
+ EndLoopAddressCoarseOffset,
+ /// Coarse tune
+ CoarseTune,
+ /// Fine tune
+ FineTune,
+ /// Sample ID
+ SampleID,
+ /// Sample modes
+ SampleModes,
+ /// Reserved
+ Reserved3,
+ /// Scale tuning
+ ScaleTuning,
+ /// Exclusive class
+ ExclusiveClass,
+ /// Overriding root key
+ OverridingRootKey,
+ /// Unused
+ Unused5,
+ /// Unused
+ UnusedEnd
+ }
+}
diff --git a/NAudio/FileFormats/SoundFont/InfoChunk.cs b/NAudio/FileFormats/SoundFont/InfoChunk.cs
new file mode 100644
index 00000000..612c359c
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/InfoChunk.cs
@@ -0,0 +1,282 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// A soundfont info chunk
+ ///
+ public class InfoChunk
+ {
+ //private RiffChunk chunk;
+ private SFVersion verSoundFont;
+ private string waveTableSoundEngine;
+ private string bankName;
+ private string dataROM;
+ private string creationDate;
+ private string author;
+ private string targetProduct;
+ private string copyright;
+ private string comments;
+ private string tools; // typically ToolUsedToCreate n.nn:MostRecentToolUsedForModification n.nn
+ private SFVersion verROM;
+
+ internal InfoChunk(RiffChunk chunk)
+ {
+ bool ifilPresent = false;
+ bool isngPresent = false;
+ bool INAMPresent = false;
+ if(chunk.ReadChunkID() != "INFO")
+ {
+ throw new InvalidDataException("Not an INFO chunk");
+ }
+ //this.chunk = chunk;
+ RiffChunk c;
+ while((c = chunk.GetNextSubChunk()) != null)
+ {
+ switch(c.ChunkID)
+ {
+ case "ifil":
+ ifilPresent = true;
+ verSoundFont = (SFVersion) c.GetDataAsStructure(new SFVersionBuilder());
+ break;
+ case "isng":
+ isngPresent = true;
+ waveTableSoundEngine = c.GetDataAsString();
+ break;
+ case "INAM":
+ INAMPresent = true;
+ bankName = c.GetDataAsString();
+ break;
+ case "irom":
+ dataROM = c.GetDataAsString();
+ break;
+ case "iver":
+ verROM = (SFVersion) c.GetDataAsStructure(new SFVersionBuilder());
+ break;
+ case "ICRD":
+ creationDate = c.GetDataAsString();
+ break;
+ case "IENG":
+ author = c.GetDataAsString();
+ break;
+ case "IPRD":
+ targetProduct = c.GetDataAsString();
+ break;
+ case "ICOP":
+ copyright = c.GetDataAsString();
+ break;
+ case "ICMT":
+ comments = c.GetDataAsString();
+ break;
+ case "ISFT":
+ tools = c.GetDataAsString();
+ break;
+ default:
+ throw new InvalidDataException(String.Format("Unknown chunk type {0}",c.ChunkID));
+ }
+ }
+ if(!ifilPresent)
+ {
+ throw new InvalidDataException("Missing SoundFont version information");
+ }
+ if(!isngPresent)
+ {
+ throw new InvalidDataException("Missing wavetable sound engine information");
+ }
+ if(!INAMPresent)
+ {
+ throw new InvalidDataException("Missing SoundFont name information");
+ }
+ }
+
+ ///
+ /// SoundFont Version
+ ///
+ public SFVersion SoundFontVersion
+ {
+ get
+ {
+ return verSoundFont;
+ }
+ }
+
+ ///
+ /// WaveTable sound engine
+ ///
+ public string WaveTableSoundEngine
+ {
+ get
+ {
+ return waveTableSoundEngine;
+ }
+ set
+ {
+ // TODO: check format
+ waveTableSoundEngine = value;
+ }
+ }
+
+ ///
+ /// Bank name
+ ///
+ public string BankName
+ {
+ get
+ {
+ return bankName;
+ }
+ set
+ {
+ // TODO: check format
+ bankName = value;
+ }
+ }
+
+ ///
+ /// Data ROM
+ ///
+ public string DataROM
+ {
+ get
+ {
+ return dataROM;
+ }
+ set {
+ // TODO: check format
+ dataROM = value;
+ }
+ }
+
+ ///
+ /// Creation Date
+ ///
+ public string CreationDate
+ {
+ get
+ {
+ return creationDate;
+ }
+ set
+ {
+ // TODO: check format
+ creationDate = value;
+ }
+ }
+
+ ///
+ /// Author
+ ///
+ public string Author
+ {
+ get
+ {
+ return author;
+ }
+ set
+ {
+ // TODO: check format
+ author = value;
+ }
+ }
+
+ ///
+ /// Target Product
+ ///
+ public string TargetProduct
+ {
+ get
+ {
+ return targetProduct;
+ }
+ set
+ {
+ // TODO: check format
+ targetProduct = value;
+ }
+ }
+
+ ///
+ /// Copyright
+ ///
+ public string Copyright
+ {
+ get
+ {
+ return copyright;
+ }
+ set
+ {
+ // TODO: check format
+ copyright = value;
+ }
+ }
+
+ ///
+ /// Comments
+ ///
+ public string Comments
+ {
+ get
+ {
+ return comments;
+ }
+ set
+ {
+ // TODO: check format
+ comments = value;
+ }
+ }
+
+ ///
+ /// Tools
+ ///
+ public string Tools
+ {
+ get
+ {
+ return tools;
+ }
+ set
+ {
+ // TODO: check format
+ tools = value;
+ }
+ }
+
+ ///
+ /// ROM Version
+ ///
+ public SFVersion ROMVersion
+ {
+ get
+ {
+ return verROM;
+ }
+ set
+ {
+ verROM = value;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ return String.Format("Bank Name: {0}\r\nAuthor: {1}\r\nCopyright: {2}\r\nCreation Date: {3}\r\nTools: {4}\r\nComments: {5}\r\nSound Engine: {6}\r\nSoundFont Version: {7}\r\nTarget Product: {8}\r\nData ROM: {9}\r\nROM Version: {10}",
+ BankName,
+ Author,
+ Copyright,
+ CreationDate,
+ Tools,
+ "TODO-fix comments",//Comments,
+ WaveTableSoundEngine,
+ SoundFontVersion,
+ TargetProduct,
+ DataROM,
+ ROMVersion);
+ }
+ }
+
+} // end of namespace
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/Instrument.cs b/NAudio/FileFormats/SoundFont/Instrument.cs
new file mode 100644
index 00000000..0c4b4d88
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/Instrument.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// SoundFont instrument
+ ///
+ public class Instrument
+ {
+ private string name;
+ internal ushort startInstrumentZoneIndex;
+ internal ushort endInstrumentZoneIndex;
+ private Zone[] zones;
+
+ ///
+ /// instrument name
+ ///
+ public string Name
+ {
+ get
+ {
+ return name;
+ }
+ set
+ {
+ // TODO: validate
+ name = value;
+ }
+ }
+
+ ///
+ /// Zones
+ ///
+ public Zone[] Zones
+ {
+ get
+ {
+ return zones;
+ }
+ set
+ {
+ zones = value;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ return this.name;
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/InstrumentBuilder.cs b/NAudio/FileFormats/SoundFont/InstrumentBuilder.cs
new file mode 100644
index 00000000..1a86a09f
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/InstrumentBuilder.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Instrument Builder
+ ///
+ internal class InstrumentBuilder : StructureBuilder
+ {
+ private Instrument lastInstrument = null;
+
+ public override Instrument Read(BinaryReader br)
+ {
+ Instrument i = new Instrument();
+ string s = Encoding.UTF8.GetString(br.ReadBytes(20), 0, 20);
+ if(s.IndexOf('\0') >= 0)
+ {
+ s = s.Substring(0,s.IndexOf('\0'));
+ }
+ i.Name = s;
+ i.startInstrumentZoneIndex = br.ReadUInt16();
+ if(lastInstrument != null)
+ {
+ lastInstrument.endInstrumentZoneIndex = (ushort) (i.startInstrumentZoneIndex - 1);
+ }
+ data.Add(i);
+ lastInstrument = i;
+ return i;
+ }
+
+ public override void Write(BinaryWriter bw, Instrument instrument)
+ {
+ }
+
+ public override int Length
+ {
+ get
+ {
+ return 22;
+ }
+ }
+
+ public void LoadZones(Zone[] zones)
+ {
+ // don't do the last preset, which is simply EOP
+ for(int instrument = 0; instrument < data.Count - 1; instrument++)
+ {
+ Instrument i = (Instrument) data[instrument];
+ i.Zones = new Zone[i.endInstrumentZoneIndex - i.startInstrumentZoneIndex + 1];
+ Array.Copy(zones,i.startInstrumentZoneIndex,i.Zones,0,i.Zones.Length);
+ }
+ // we can get rid of the EOP record now
+ data.RemoveAt(data.Count - 1);
+ }
+
+ public Instrument[] Instruments
+ {
+ get
+ {
+ return data.ToArray();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/Modulator.cs b/NAudio/FileFormats/SoundFont/Modulator.cs
new file mode 100644
index 00000000..42d6f06b
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/Modulator.cs
@@ -0,0 +1,114 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Transform Types
+ ///
+ public enum TransformEnum
+ {
+ ///
+ /// Linear
+ ///
+ Linear = 0
+ }
+
+ ///
+ /// Modulator
+ ///
+ public class Modulator
+ {
+ private ModulatorType sourceModulationData;
+ private GeneratorEnum destinationGenerator;
+ private short amount;
+ private ModulatorType sourceModulationAmount;
+ private TransformEnum sourceTransform;
+
+ ///
+ /// Source Modulation data type
+ ///
+ public ModulatorType SourceModulationData
+ {
+ get
+ {
+ return sourceModulationData;
+ }
+ set
+ {
+ sourceModulationData = value;
+ }
+ }
+
+ ///
+ /// Destination generator type
+ ///
+ public GeneratorEnum DestinationGenerator
+ {
+ get
+ {
+ return destinationGenerator;
+ }
+ set
+ {
+ destinationGenerator = value;
+ }
+ }
+
+ ///
+ /// Amount
+ ///
+ public short Amount
+ {
+ get
+ {
+ return amount;
+ }
+ set
+ {
+ amount = value;
+ }
+ }
+
+ ///
+ /// Source Modulation Amount Type
+ ///
+ public ModulatorType SourceModulationAmount
+ {
+ get
+ {
+ return sourceModulationAmount;
+ }
+ set
+ {
+ sourceModulationAmount = value;
+ }
+ }
+
+ ///
+ /// Source Transform Type
+ ///
+ public TransformEnum SourceTransform
+ {
+ get
+ {
+ return sourceTransform;
+ }
+ set
+ {
+ sourceTransform = value;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ return String.Format("Modulator {0} {1} {2} {3} {4}",
+ sourceModulationData,destinationGenerator,
+ amount,sourceModulationAmount,sourceTransform);
+
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/ModulatorBuilder.cs b/NAudio/FileFormats/SoundFont/ModulatorBuilder.cs
new file mode 100644
index 00000000..877cfb47
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/ModulatorBuilder.cs
@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont {
+ class ModulatorBuilder : StructureBuilder {
+ public override Modulator Read(BinaryReader br)
+ {
+ Modulator m = new Modulator();
+ m.SourceModulationData = new ModulatorType(br.ReadUInt16());
+ m.DestinationGenerator = (GeneratorEnum) br.ReadUInt16();
+ m.Amount = br.ReadInt16();
+ m.SourceModulationAmount = new ModulatorType(br.ReadUInt16());
+ m.SourceTransform = (TransformEnum) br.ReadUInt16();
+ data.Add(m);
+ return m;
+ }
+
+ public override void Write(BinaryWriter bw, Modulator o)
+ {
+ //Zone z = (Zone) o;
+ //bw.Write(p.---);
+ }
+
+ public override int Length {
+ get {
+ return 10;
+ }
+ }
+
+ public Modulator[] Modulators
+ {
+ get
+ {
+ return data.ToArray();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/ModulatorType.cs b/NAudio/FileFormats/SoundFont/ModulatorType.cs
new file mode 100644
index 00000000..8487f441
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/ModulatorType.cs
@@ -0,0 +1,101 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Controller Sources
+ ///
+ public enum ControllerSourceEnum
+ {
+ ///
+ /// No Controller
+ ///
+ NoController = 0,
+ ///
+ /// Note On Velocity
+ ///
+ NoteOnVelocity = 2,
+ ///
+ /// Note On Key Number
+ ///
+ NoteOnKeyNumber = 3,
+ ///
+ /// Poly Pressure
+ ///
+ PolyPressure = 10,
+ ///
+ /// Channel Pressure
+ ///
+ ChannelPressure = 13,
+ ///
+ /// Pitch Wheel
+ ///
+ PitchWheel = 14,
+ ///
+ /// Pitch Wheel Sensitivity
+ ///
+ PitchWheelSensitivity = 16
+ }
+
+ ///
+ /// Source Types
+ ///
+ public enum SourceTypeEnum
+ {
+ ///
+ /// Linear
+ ///
+ Linear,
+ ///
+ /// Concave
+ ///
+ Concave,
+ ///
+ /// Convex
+ ///
+ Convex,
+ ///
+ /// Switch
+ ///
+ Switch
+ }
+
+ ///
+ /// Modulator Type
+ ///
+ public class ModulatorType
+ {
+ bool polarity;
+ bool direction;
+ bool midiContinuousController;
+ ControllerSourceEnum controllerSource;
+ SourceTypeEnum sourceType;
+ ushort midiContinuousControllerNumber;
+
+ internal ModulatorType(ushort raw)
+ {
+ // TODO: map this to fields
+ polarity = ((raw & 0x0200) == 0x0200);
+ direction = ((raw & 0x0100) == 0x0100);
+ midiContinuousController = ((raw & 0x0080) == 0x0080);
+ sourceType = (SourceTypeEnum) ((raw & (0xFC00)) >> 10);
+
+ controllerSource = (ControllerSourceEnum) (raw & 0x007F);
+ midiContinuousControllerNumber = (ushort) (raw & 0x007F);
+
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ if(midiContinuousController)
+ return String.Format("{0} CC{1}",sourceType,midiContinuousControllerNumber);
+ else
+ return String.Format("{0} {1}",sourceType,controllerSource);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/Preset.cs b/NAudio/FileFormats/SoundFont/Preset.cs
new file mode 100644
index 00000000..8a8f826f
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/Preset.cs
@@ -0,0 +1,92 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// A SoundFont Preset
+ ///
+ public class Preset
+ {
+ private string name;
+ private ushort patchNumber;
+ private ushort bank;
+ internal ushort startPresetZoneIndex;
+ internal ushort endPresetZoneIndex;
+ internal uint library;
+ internal uint genre;
+ internal uint morphology;
+ private Zone[] zones;
+
+ ///
+ /// Preset name
+ ///
+ public string Name
+ {
+ get
+ {
+ return name;
+ }
+ set
+ {
+ // TODO: validate
+ name = value;
+ }
+ }
+
+ ///
+ /// Patch Number
+ ///
+ public ushort PatchNumber
+ {
+ get
+ {
+ return patchNumber;
+ }
+ set
+ {
+ // TODO: validate
+ patchNumber = value;
+ }
+ }
+
+ ///
+ /// Bank number
+ ///
+ public ushort Bank
+ {
+ get
+ {
+ return bank;
+ }
+ set
+ {
+ // 0 - 127, GM percussion bank is 128
+ // TODO: validate
+ bank = value;
+ }
+ }
+
+ ///
+ /// Zones
+ ///
+ public Zone[] Zones
+ {
+ get
+ {
+ return zones;
+ }
+ set
+ {
+ zones = value;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ return String.Format("{0}-{1} {2}",bank,patchNumber,name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/PresetBuilder.cs b/NAudio/FileFormats/SoundFont/PresetBuilder.cs
new file mode 100644
index 00000000..d3c97be7
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/PresetBuilder.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.SoundFont
+{
+ class PresetBuilder : StructureBuilder
+ {
+ private Preset lastPreset = null;
+
+ public override Preset Read(BinaryReader br)
+ {
+ Preset p = new Preset();
+ string s = Encoding.UTF8.GetString(br.ReadBytes(20), 0, 20);
+ if(s.IndexOf('\0') >= 0) {
+ s = s.Substring(0,s.IndexOf('\0'));
+ }
+ p.Name = s;
+ p.PatchNumber = br.ReadUInt16();
+ p.Bank = br.ReadUInt16();
+ p.startPresetZoneIndex = br.ReadUInt16();
+ p.library = br.ReadUInt32();
+ p.genre = br.ReadUInt32();
+ p.morphology = br.ReadUInt32();
+ if(lastPreset != null)
+ lastPreset.endPresetZoneIndex = (ushort) (p.startPresetZoneIndex - 1);
+ data.Add(p);
+ lastPreset = p;
+ return p;
+ }
+
+ public override void Write(BinaryWriter bw, Preset preset)
+ {
+ }
+
+ public override int Length {
+ get {
+ return 38;
+ }
+ }
+
+ public void LoadZones(Zone[] presetZones)
+ {
+ // don't do the last preset, which is simply EOP
+ for(int preset = 0; preset < data.Count - 1; preset++)
+ {
+ Preset p = (Preset) data[preset];
+ p.Zones = new Zone[p.endPresetZoneIndex - p.startPresetZoneIndex + 1];
+ Array.Copy(presetZones,p.startPresetZoneIndex,p.Zones,0,p.Zones.Length);
+ }
+ // we can get rid of the EOP record now
+ data.RemoveAt(data.Count - 1);
+ }
+
+ public Preset[] Presets
+ {
+ get
+ {
+ return data.ToArray();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/PresetsChunk.cs b/NAudio/FileFormats/SoundFont/PresetsChunk.cs
new file mode 100644
index 00000000..72b5daa2
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/PresetsChunk.cs
@@ -0,0 +1,136 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Class to read the SoundFont file presets chunk
+ ///
+ public class PresetsChunk
+ {
+ private PresetBuilder presetHeaders = new PresetBuilder();
+ private ZoneBuilder presetZones = new ZoneBuilder();
+ private ModulatorBuilder presetZoneModulators = new ModulatorBuilder();
+ private GeneratorBuilder presetZoneGenerators = new GeneratorBuilder();
+ private InstrumentBuilder instruments = new InstrumentBuilder();
+ private ZoneBuilder instrumentZones = new ZoneBuilder();
+ private ModulatorBuilder instrumentZoneModulators = new ModulatorBuilder();
+ private GeneratorBuilder instrumentZoneGenerators = new GeneratorBuilder();
+ private SampleHeaderBuilder sampleHeaders = new SampleHeaderBuilder();
+
+ internal PresetsChunk(RiffChunk chunk)
+ {
+ string header = chunk.ReadChunkID();
+ if(header != "pdta")
+ {
+ throw new InvalidDataException(String.Format("Not a presets data chunk ({0})",header));
+ }
+
+ RiffChunk c;
+ while((c = chunk.GetNextSubChunk()) != null)
+ {
+ switch(c.ChunkID) {
+ case "PHDR":
+ case "phdr":
+ c.GetDataAsStructureArray(presetHeaders);
+ break;
+ case "PBAG":
+ case "pbag":
+ c.GetDataAsStructureArray(presetZones);
+ break;
+ case "PMOD":
+ case "pmod":
+ c.GetDataAsStructureArray(presetZoneModulators);
+ break;
+ case "PGEN":
+ case "pgen":
+ c.GetDataAsStructureArray(presetZoneGenerators);
+ break;
+ case "INST":
+ case "inst":
+ c.GetDataAsStructureArray(instruments);
+ break;
+ case "IBAG":
+ case "ibag":
+ c.GetDataAsStructureArray(instrumentZones);
+ break;
+ case "IMOD":
+ case "imod":
+ c.GetDataAsStructureArray(instrumentZoneModulators);
+ break;
+ case "IGEN":
+ case "igen":
+ c.GetDataAsStructureArray(instrumentZoneGenerators);
+ break;
+ case "SHDR":
+ case "shdr":
+ c.GetDataAsStructureArray(sampleHeaders);
+ break;
+ default:
+ throw new InvalidDataException(String.Format("Unknown chunk type {0}", c.ChunkID));
+ }
+ }
+
+ // now link things up
+ instrumentZoneGenerators.Load(sampleHeaders.SampleHeaders);
+ instrumentZones.Load(instrumentZoneModulators.Modulators,instrumentZoneGenerators.Generators);
+ instruments.LoadZones(instrumentZones.Zones);
+ presetZoneGenerators.Load(instruments.Instruments);
+ presetZones.Load(presetZoneModulators.Modulators,presetZoneGenerators.Generators);
+ presetHeaders.LoadZones(presetZones.Zones);
+ sampleHeaders.RemoveEOS();
+ }
+
+ ///
+ /// The Presets contained in this chunk
+ ///
+ public Preset[] Presets
+ {
+ get
+ {
+ return presetHeaders.Presets;
+ }
+ }
+
+ ///
+ /// The instruments contained in this chunk
+ ///
+ public Instrument[] Instruments
+ {
+ get
+ {
+ return instruments.Instruments;
+ }
+ }
+
+ ///
+ /// The sample headers contained in this chunk
+ ///
+ public SampleHeader[] SampleHeaders
+ {
+ get
+ {
+ return sampleHeaders.SampleHeaders;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append("Preset Headers:\r\n");
+ foreach(Preset p in presetHeaders.Presets) {
+ sb.AppendFormat("{0}\r\n",p);
+ }
+ sb.Append("Instruments:\r\n");
+ foreach(Instrument i in instruments.Instruments) {
+ sb.AppendFormat("{0}\r\n",i);
+ }
+ return sb.ToString();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/RiffChunk.cs b/NAudio/FileFormats/SoundFont/RiffChunk.cs
new file mode 100644
index 00000000..282f5b87
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/RiffChunk.cs
@@ -0,0 +1,164 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.SoundFont
+{
+ internal class RiffChunk
+ {
+ private string chunkID;
+ private uint chunkSize;
+ private long dataOffset; // data offset in the file
+ private BinaryReader riffFile;
+
+ public static RiffChunk GetTopLevelChunk(BinaryReader file)
+ {
+ RiffChunk r = new RiffChunk(file);
+ r.ReadChunk();
+ return r;
+ }
+
+ private RiffChunk(BinaryReader file)
+ {
+ riffFile = file;
+ chunkID = "????";
+ chunkSize = 0;
+ dataOffset = 0;
+ }
+
+ ///
+ /// just reads a chunk ID at the current position
+ ///
+ /// chunk ID
+ public string ReadChunkID()
+ {
+ byte []cid = riffFile.ReadBytes(4);
+ if(cid.Length != 4)
+ {
+ throw new InvalidDataException("Couldn't read Chunk ID");
+ }
+ return ByteEncoding.Instance.GetString(cid, 0, cid.Length);
+ }
+
+ ///
+ /// reads a chunk at the current position
+ ///
+ private void ReadChunk()
+ {
+ this.chunkID = ReadChunkID();
+ this.chunkSize = riffFile.ReadUInt32(); //(uint) IPAddress.NetworkToHostOrder(riffFile.ReadUInt32());
+ this.dataOffset = riffFile.BaseStream.Position;
+ }
+
+ ///
+ /// creates a new riffchunk from current position checking that we're not
+ /// at the end of this chunk first
+ ///
+ /// the new chunk
+ public RiffChunk GetNextSubChunk()
+ {
+ if(riffFile.BaseStream.Position + 8 < dataOffset + chunkSize)
+ {
+ RiffChunk chunk = new RiffChunk(riffFile);
+ chunk.ReadChunk();
+ return chunk;
+ }
+ //Console.WriteLine("DEBUG Failed to GetNextSubChunk because Position is {0}, dataOffset{1}, chunkSize {2}",riffFile.BaseStream.Position,dataOffset,chunkSize);
+ return null;
+ }
+
+ public byte[] GetData()
+ {
+ riffFile.BaseStream.Position = dataOffset;
+ byte[] data = riffFile.ReadBytes((int) chunkSize);
+ if(data.Length != chunkSize)
+ {
+ throw new InvalidDataException(String.Format("Couldn't read chunk's data Chunk: {0}, read {1} bytes",this,data.Length));
+ }
+ return data;
+ }
+
+ ///
+ /// useful for chunks that just contain a string
+ ///
+ /// chunk as string
+ public string GetDataAsString()
+ {
+ byte[] data = GetData();
+ if(data == null)
+ return null;
+ return ByteEncoding.Instance.GetString(data, 0, data.Length);
+ }
+
+ public T GetDataAsStructure(StructureBuilder s)
+ {
+ riffFile.BaseStream.Position = dataOffset;
+ if(s.Length != chunkSize)
+ {
+ throw new InvalidDataException(String.Format("Chunk size is: {0} so can't read structure of: {1}",chunkSize,s.Length));
+ }
+ return s.Read(riffFile);
+ }
+
+ public T[] GetDataAsStructureArray(StructureBuilder s)
+ {
+ riffFile.BaseStream.Position = dataOffset;
+ if(chunkSize % s.Length != 0)
+ {
+ throw new InvalidDataException(String.Format("Chunk size is: {0} not a multiple of structure size: {1}", chunkSize, s.Length));
+ }
+ int structuresToRead = (int) (chunkSize / s.Length);
+ T[] a = new T[structuresToRead];
+ for(int n = 0; n < structuresToRead; n++)
+ {
+ a[n] = s.Read(riffFile);
+ }
+ return a;
+ }
+
+ public string ChunkID
+ {
+ get
+ {
+ return chunkID;
+ }
+ set
+ {
+ if(value == null)
+ {
+ throw new ArgumentNullException("ChunkID may not be null");
+ }
+ if(value.Length != 4)
+ {
+ throw new ArgumentException("ChunkID must be four characters");
+ }
+ chunkID = value;
+ }
+ }
+
+ public uint ChunkSize
+ {
+ get
+ {
+ return chunkSize;
+ }
+ }
+
+ public long DataOffset
+ {
+ get
+ {
+ return dataOffset;
+ }
+ }
+
+ public override string ToString()
+ {
+ return String.Format("RiffChunk ID: {0} Size: {1} Data Offset: {2}",ChunkID,ChunkSize,DataOffset);
+ }
+
+ }
+
+}
diff --git a/NAudio/FileFormats/SoundFont/SFSampleLink.cs b/NAudio/FileFormats/SoundFont/SFSampleLink.cs
new file mode 100644
index 00000000..de98a1ab
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SFSampleLink.cs
@@ -0,0 +1,41 @@
+namespace NAudio.SoundFont
+{
+ ///
+ /// Sample Link Type
+ ///
+ public enum SFSampleLink : ushort
+ {
+ ///
+ /// Mono Sample
+ ///
+ MonoSample = 1,
+ ///
+ /// Right Sample
+ ///
+ RightSample = 2,
+ ///
+ /// Left Sample
+ ///
+ LeftSample = 4,
+ ///
+ /// Linked Sample
+ ///
+ LinkedSample = 8,
+ ///
+ /// ROM Mono Sample
+ ///
+ RomMonoSample = 0x8001,
+ ///
+ /// ROM Right Sample
+ ///
+ RomRightSample = 0x8002,
+ ///
+ /// ROM Left Sample
+ ///
+ RomLeftSample = 0x8004,
+ ///
+ /// ROM Linked Sample
+ ///
+ RomLinkedSample = 0x8008
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/SFVersion.cs b/NAudio/FileFormats/SoundFont/SFVersion.cs
new file mode 100644
index 00000000..917cf0c5
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SFVersion.cs
@@ -0,0 +1,44 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// SoundFont Version Structure
+ ///
+ public class SFVersion
+ {
+ private short major;
+ private short minor;
+
+ ///
+ /// Major Version
+ ///
+ public short Major
+ {
+ get
+ {
+ return major;
+ }
+ set
+ {
+ major = value;
+ }
+ }
+
+ ///
+ /// Minor Version
+ ///
+ public short Minor
+ {
+ get
+ {
+ return minor;
+ }
+ set
+ {
+ minor = value;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/SFVersionBuilder.cs b/NAudio/FileFormats/SoundFont/SFVersionBuilder.cs
new file mode 100644
index 00000000..01b45480
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SFVersionBuilder.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Builds a SoundFont version
+ ///
+ class SFVersionBuilder : StructureBuilder
+ {
+ ///
+ /// Reads a SoundFont Version structure
+ ///
+ public override SFVersion Read(BinaryReader br)
+ {
+ SFVersion v = new SFVersion();
+ v.Major = br.ReadInt16();
+ v.Minor = br.ReadInt16();
+ data.Add(v);
+ return v;
+ }
+
+ ///
+ /// Writes a SoundFont Version structure
+ ///
+ public override void Write(BinaryWriter bw, SFVersion v)
+ {
+ bw.Write(v.Major);
+ bw.Write(v.Minor);
+ }
+
+ ///
+ /// Gets the length of this structure
+ ///
+ public override int Length
+ {
+ get
+ {
+ return 4;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/SampleDataChunk.cs b/NAudio/FileFormats/SoundFont/SampleDataChunk.cs
new file mode 100644
index 00000000..6a896085
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SampleDataChunk.cs
@@ -0,0 +1,28 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont
+{
+ class SampleDataChunk
+ {
+ private byte[] sampleData;
+ public SampleDataChunk(RiffChunk chunk)
+ {
+ string header = chunk.ReadChunkID();
+ if(header != "sdta")
+ {
+ throw new InvalidDataException(String.Format("Not a sample data chunk ({0})",header));
+ }
+ sampleData = chunk.GetData();
+ }
+
+ public byte[] SampleData
+ {
+ get
+ {
+ return sampleData;
+ }
+ }
+ }
+
+} // end of namespace
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/SampleHeader.cs b/NAudio/FileFormats/SoundFont/SampleHeader.cs
new file mode 100644
index 00000000..145c3e3a
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SampleHeader.cs
@@ -0,0 +1,61 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// A SoundFont Sample Header
+ ///
+ public class SampleHeader
+ {
+ ///
+ /// The sample name
+ ///
+ public string SampleName;
+ ///
+ /// Start offset
+ ///
+ public uint Start;
+ ///
+ /// End offset
+ ///
+ public uint End;
+ ///
+ /// Start loop point
+ ///
+ public uint StartLoop;
+ ///
+ /// End loop point
+ ///
+ public uint EndLoop;
+ ///
+ /// Sample Rate
+ ///
+ public uint SampleRate;
+ ///
+ /// Original pitch
+ ///
+ public byte OriginalPitch;
+ ///
+ /// Pitch correction
+ ///
+ public sbyte PitchCorrection;
+ ///
+ /// Sample Link
+ ///
+ public ushort SampleLink;
+ ///
+ /// SoundFont Sample Link Type
+ ///
+ public SFSampleLink SFSampleLink;
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ return SampleName;
+ }
+
+ }
+}
+
diff --git a/NAudio/FileFormats/SoundFont/SampleHeaderBuilder.cs b/NAudio/FileFormats/SoundFont/SampleHeaderBuilder.cs
new file mode 100644
index 00000000..3f30ea6b
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SampleHeaderBuilder.cs
@@ -0,0 +1,54 @@
+using System;
+using System.IO;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.SoundFont
+{
+ class SampleHeaderBuilder : StructureBuilder
+ {
+ public override SampleHeader Read(BinaryReader br)
+ {
+ SampleHeader sh = new SampleHeader();
+ var s = br.ReadBytes(20);
+
+ sh.SampleName = ByteEncoding.Instance.GetString(s, 0, s.Length);
+ sh.Start = br.ReadUInt32();
+ sh.End = br.ReadUInt32();
+ sh.StartLoop = br.ReadUInt32();
+ sh.EndLoop = br.ReadUInt32();
+ sh.SampleRate = br.ReadUInt32();
+ sh.OriginalPitch = br.ReadByte();
+ sh.PitchCorrection = br.ReadSByte();
+ sh.SampleLink = br.ReadUInt16();
+ sh.SFSampleLink = (SFSampleLink) br.ReadUInt16();
+ data.Add(sh);
+ return sh;
+ }
+
+ public override void Write(BinaryWriter bw, SampleHeader sampleHeader)
+ {
+ }
+
+ public override int Length
+ {
+ get
+ {
+ return 46;
+ }
+ }
+
+ internal void RemoveEOS()
+ {
+ data.RemoveAt(data.Count-1);
+ }
+
+ public SampleHeader[] SampleHeaders
+ {
+ get
+ {
+ return data.ToArray();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/SampleMode.cs b/NAudio/FileFormats/SoundFont/SampleMode.cs
new file mode 100644
index 00000000..4c27e142
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SampleMode.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// SoundFont sample modes
+ ///
+ public enum SampleMode
+ {
+ ///
+ /// No loop
+ ///
+ NoLoop,
+ ///
+ /// Loop Continuously
+ ///
+ LoopContinuously,
+ ///
+ /// Reserved no loop
+ ///
+ ReservedNoLoop,
+ ///
+ /// Loop and continue
+ ///
+ LoopAndContinue
+ }
+}
diff --git a/NAudio/FileFormats/SoundFont/SoundFont.cs b/NAudio/FileFormats/SoundFont/SoundFont.cs
new file mode 100644
index 00000000..a2c55386
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/SoundFont.cs
@@ -0,0 +1,132 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// Represents a SoundFont
+ ///
+ public class SoundFont
+ {
+ private InfoChunk info;
+ private PresetsChunk presetsChunk;
+ private SampleDataChunk sampleData;
+
+#if !NETFX_CORE
+ ///
+ /// Loads a SoundFont from a file
+ ///
+ /// Filename of the SoundFont
+ public SoundFont(string fileName) :
+ this(new FileStream(fileName,FileMode.Open,FileAccess.Read))
+ {
+ }
+#endif
+
+ ///
+ /// Loads a SoundFont from a stream
+ ///
+ /// stream
+ public SoundFont(Stream sfFile)
+ {
+ using(sfFile) // a bit ugly, done to get Win store to compile
+ {
+ RiffChunk riff = RiffChunk.GetTopLevelChunk(new BinaryReader(sfFile));
+ if(riff.ChunkID == "RIFF")
+ {
+ string formHeader = riff.ReadChunkID();
+ if(formHeader != "sfbk")
+ {
+ throw new InvalidDataException(String.Format("Not a SoundFont ({0})",formHeader));
+ }
+ RiffChunk list = riff.GetNextSubChunk();
+ if(list.ChunkID == "LIST")
+ {
+ //RiffChunk r = list.GetNextSubChunk();
+ info = new InfoChunk(list);
+
+ RiffChunk r = riff.GetNextSubChunk();
+ sampleData = new SampleDataChunk(r);
+
+ r = riff.GetNextSubChunk();
+ presetsChunk = new PresetsChunk(r);
+ }
+ else
+ {
+ throw new InvalidDataException(String.Format("Not info list found ({0})", list.ChunkID));
+ }
+ }
+ else
+ {
+ throw new InvalidDataException("Not a RIFF file");
+ }
+ }
+ }
+
+ ///
+ /// The File Info Chunk
+ ///
+ public InfoChunk FileInfo
+ {
+ get
+ {
+ return info;
+ }
+ }
+
+ ///
+ /// The Presets
+ ///
+ public Preset[] Presets
+ {
+ get
+ {
+ return presetsChunk.Presets;
+ }
+ }
+
+ ///
+ /// The Instruments
+ ///
+ public Instrument[] Instruments
+ {
+ get
+ {
+ return presetsChunk.Instruments;
+ }
+ }
+
+ ///
+ /// The Sample Headers
+ ///
+ public SampleHeader[] SampleHeaders
+ {
+ get
+ {
+ return presetsChunk.SampleHeaders;
+ }
+ }
+
+ ///
+ /// The Sample Data
+ ///
+ public byte[] SampleData
+ {
+ get
+ {
+ return sampleData.SampleData;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ return String.Format("Info Chunk:\r\n{0}\r\nPresets Chunk:\r\n{1}",
+ info,presetsChunk);
+ }
+
+ // TODO: save / save as function
+ }
+}
diff --git a/NAudio/FileFormats/SoundFont/StructureBuilder.cs b/NAudio/FileFormats/SoundFont/StructureBuilder.cs
new file mode 100644
index 00000000..4c9ca157
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/StructureBuilder.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Collections;
+
+namespace NAudio.SoundFont
+{
+
+ ///
+ /// base class for structures that can read themselves
+ ///
+ internal abstract class StructureBuilder
+ {
+ protected List data;
+
+ public StructureBuilder()
+ {
+ Reset();
+ }
+
+ public abstract T Read(BinaryReader br);
+ public abstract void Write(BinaryWriter bw,T o);
+ public abstract int Length { get; }
+
+ public void Reset()
+ {
+ data = new List();
+ }
+
+ public T[] Data
+ {
+ get
+ {
+ return data.ToArray();
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/Zone.cs b/NAudio/FileFormats/SoundFont/Zone.cs
new file mode 100644
index 00000000..604de6ad
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/Zone.cs
@@ -0,0 +1,57 @@
+using System;
+
+namespace NAudio.SoundFont
+{
+ ///
+ /// A SoundFont zone
+ ///
+ public class Zone
+ {
+ internal ushort generatorIndex;
+ internal ushort modulatorIndex;
+ internal ushort generatorCount;
+ internal ushort modulatorCount;
+ private Modulator[] modulators;
+ private Generator[] generators;
+
+ ///
+ ///
+ ///
+ public override string ToString()
+ {
+ return String.Format("Zone {0} Gens:{1} {2} Mods:{3}",generatorCount,generatorIndex,
+ modulatorCount,modulatorIndex);
+ }
+
+ ///
+ /// Modulators for this Zone
+ ///
+ public Modulator[] Modulators
+ {
+ get
+ {
+ return modulators;
+ }
+ set
+ {
+ modulators = value;
+ }
+ }
+
+ ///
+ /// Generators for this Zone
+ ///
+ public Generator[] Generators
+ {
+ get
+ {
+ return generators;
+ }
+ set
+ {
+ generators = value;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/SoundFont/ZoneBuilder.cs b/NAudio/FileFormats/SoundFont/ZoneBuilder.cs
new file mode 100644
index 00000000..f09e674e
--- /dev/null
+++ b/NAudio/FileFormats/SoundFont/ZoneBuilder.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+
+namespace NAudio.SoundFont
+{
+ class ZoneBuilder : StructureBuilder
+ {
+ private Zone lastZone = null;
+
+ public override Zone Read(BinaryReader br)
+ {
+ Zone z = new Zone();
+ z.generatorIndex = br.ReadUInt16();
+ z.modulatorIndex = br.ReadUInt16();
+ if(lastZone != null)
+ {
+ lastZone.generatorCount = (ushort) (z.generatorIndex - lastZone.generatorIndex);
+ lastZone.modulatorCount = (ushort) (z.modulatorIndex - lastZone.modulatorIndex);
+ }
+ data.Add(z);
+ lastZone = z;
+ return z;
+ }
+
+ public override void Write(BinaryWriter bw, Zone zone)
+ {
+ //bw.Write(p.---);
+ }
+
+ public void Load(Modulator[] modulators, Generator[] generators)
+ {
+ // don't do the last zone, which is simply EOZ
+ for(int zone = 0; zone < data.Count - 1; zone++)
+ {
+ Zone z = (Zone) data[zone];
+ z.Generators = new Generator[z.generatorCount];
+ Array.Copy(generators,z.generatorIndex,z.Generators,0,z.generatorCount);
+ z.Modulators = new Modulator[z.modulatorCount];
+ Array.Copy(modulators,z.modulatorIndex,z.Modulators,0,z.modulatorCount);
+ }
+ // we can get rid of the EOP record now
+ data.RemoveAt(data.Count - 1);
+ }
+
+ public Zone[] Zones
+ {
+ get
+ {
+ return data.ToArray();
+ }
+ }
+
+ public override int Length {
+ get {
+ return 4;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/FileFormats/Wav/WaveFileChunkReader.cs b/NAudio/FileFormats/Wav/WaveFileChunkReader.cs
new file mode 100644
index 00000000..930f9a72
--- /dev/null
+++ b/NAudio/FileFormats/Wav/WaveFileChunkReader.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using NAudio.Utils;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudio.FileFormats.Wav
+{
+ class WaveFileChunkReader
+ {
+ private WaveFormat waveFormat;
+ private long dataChunkPosition;
+ private long dataChunkLength;
+ private List riffChunks;
+ private readonly bool strictMode;
+ private bool isRf64;
+ private readonly bool storeAllChunks;
+ private long riffSize;
+
+ public WaveFileChunkReader()
+ {
+ storeAllChunks = true;
+ strictMode = false;
+ }
+
+ public void ReadWaveHeader(Stream stream)
+ {
+ this.dataChunkPosition = -1;
+ this.waveFormat = null;
+ this.riffChunks = new List();
+ this.dataChunkLength = 0;
+
+ var br = new BinaryReader(stream);
+ ReadRiffHeader(br);
+ this.riffSize = br.ReadUInt32(); // read the file size (minus 8 bytes)
+
+ if (br.ReadInt32() != ChunkIdentifier.ChunkIdentifierToInt32("WAVE"))
+ {
+ throw new FormatException("Not a WAVE file - no WAVE header");
+ }
+
+ if (isRf64)
+ {
+ ReadDs64Chunk(br);
+ }
+
+ int dataChunkId = ChunkIdentifier.ChunkIdentifierToInt32("data");
+ int formatChunkId = ChunkIdentifier.ChunkIdentifierToInt32("fmt ");
+
+ // sometimes a file has more data than is specified after the RIFF header
+ long stopPosition = Math.Min(riffSize + 8, stream.Length);
+
+ // this -8 is so we can be sure that there are at least 8 bytes for a chunk id and length
+ while (stream.Position <= stopPosition - 8)
+ {
+ Int32 chunkIdentifier = br.ReadInt32();
+ var chunkLength = br.ReadUInt32();
+ if (chunkIdentifier == dataChunkId)
+ {
+ dataChunkPosition = stream.Position;
+ if (!isRf64) // we already know the dataChunkLength if this is an RF64 file
+ {
+ dataChunkLength = chunkLength;
+ }
+ stream.Position += chunkLength;
+ }
+ else if (chunkIdentifier == formatChunkId)
+ {
+ if (chunkLength > Int32.MaxValue)
+ throw new InvalidDataException(string.Format("Format chunk length must be between 0 and {0}.", Int32.MaxValue));
+ waveFormat = WaveFormat.FromFormatChunk(br, (int)chunkLength);
+ }
+ else
+ {
+ // check for invalid chunk length
+ if (chunkLength > stream.Length - stream.Position)
+ {
+ if (strictMode)
+ {
+ Debug.Assert(false, String.Format("Invalid chunk length {0}, pos: {1}. length: {2}",
+ chunkLength, stream.Position, stream.Length));
+ }
+ // an exception will be thrown further down if we haven't got a format and data chunk yet,
+ // otherwise we will tolerate this file despite it having corrupt data at the end
+ break;
+ }
+ if (storeAllChunks)
+ {
+ if (chunkLength > Int32.MaxValue)
+ throw new InvalidDataException(string.Format("RiffChunk chunk length must be between 0 and {0}.", Int32.MaxValue));
+ riffChunks.Add(GetRiffChunk(stream, chunkIdentifier, (int)chunkLength));
+ }
+ stream.Position += chunkLength;
+ }
+ }
+
+ if (waveFormat == null)
+ {
+ throw new FormatException("Invalid WAV file - No fmt chunk found");
+ }
+ if (dataChunkPosition == -1)
+ {
+ throw new FormatException("Invalid WAV file - No data chunk found");
+ }
+ }
+
+ ///
+ /// http://tech.ebu.ch/docs/tech/tech3306-2009.pdf
+ ///
+ private void ReadDs64Chunk(BinaryReader reader)
+ {
+ int ds64ChunkId = ChunkIdentifier.ChunkIdentifierToInt32("ds64");
+ int chunkId = reader.ReadInt32();
+ if (chunkId != ds64ChunkId)
+ {
+ throw new FormatException("Invalid RF64 WAV file - No ds64 chunk found");
+ }
+ int chunkSize = reader.ReadInt32();
+ this.riffSize = reader.ReadInt64();
+ this.dataChunkLength = reader.ReadInt64();
+ long sampleCount = reader.ReadInt64(); // replaces the value in the fact chunk
+ reader.ReadBytes(chunkSize - 24); // get to the end of this chunk (should parse extra stuff later)
+ }
+
+ private static RiffChunk GetRiffChunk(Stream stream, Int32 chunkIdentifier, Int32 chunkLength)
+ {
+ return new RiffChunk(chunkIdentifier, chunkLength, stream.Position);
+ }
+
+ private void ReadRiffHeader(BinaryReader br)
+ {
+ int header = br.ReadInt32();
+ if (header == ChunkIdentifier.ChunkIdentifierToInt32("RF64"))
+ {
+ this.isRf64 = true;
+ }
+ else if (header != ChunkIdentifier.ChunkIdentifierToInt32("RIFF"))
+ {
+ throw new FormatException("Not a WAVE file - no RIFF header");
+ }
+ }
+
+ ///
+ /// WaveFormat
+ ///
+ public WaveFormat WaveFormat { get { return this.waveFormat; } }
+
+ ///
+ /// Data Chunk Position
+ ///
+ public long DataChunkPosition { get { return this.dataChunkPosition; } }
+
+ ///
+ /// Data Chunk Length
+ ///
+ public long DataChunkLength { get { return this.dataChunkLength; } }
+
+ ///
+ /// Riff Chunks
+ ///
+ public List RiffChunks { get { return this.riffChunks; } }
+ }
+}
diff --git a/NAudio/Gui/Fader.cs b/NAudio/Gui/Fader.cs
new file mode 100644
index 00000000..e0de860e
--- /dev/null
+++ b/NAudio/Gui/Fader.cs
@@ -0,0 +1,222 @@
+using System;
+using System.ComponentModel;
+using System.Collections;
+using System.Diagnostics;
+using System.Windows.Forms;
+using System.Drawing;
+
+namespace NAudio.Gui
+{
+ ///
+ /// Summary description for Fader.
+ ///
+ public class Fader : System.Windows.Forms.Control
+ {
+ private int minimum;
+ private int maximum;
+ private float percent;
+ private Orientation orientation;
+
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.Container components = null;
+
+ ///
+ /// Creates a new Fader control
+ ///
+ public Fader()
+ {
+ // Required for Windows.Forms Class Composition Designer support
+ InitializeComponent();
+
+ this.SetStyle(ControlStyles.DoubleBuffer |
+ ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.UserPaint,true);
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ private readonly int SliderHeight = 30;
+ private readonly int SliderWidth = 15;
+ private Rectangle sliderRectangle = new Rectangle();
+
+ private void DrawSlider(Graphics g)
+ {
+ Brush block = new SolidBrush(Color.White);
+ Pen centreLine = new Pen(Color.Black);
+ sliderRectangle.X = (this.Width - SliderWidth) / 2;
+ sliderRectangle.Width = SliderWidth;
+ sliderRectangle.Y = (int) ((this.Height - SliderHeight) * percent);
+ sliderRectangle.Height = SliderHeight;
+
+ g.FillRectangle(block,sliderRectangle);
+ g.DrawLine(centreLine,sliderRectangle.Left,sliderRectangle.Top + sliderRectangle.Height/2,sliderRectangle.Right,sliderRectangle.Top + sliderRectangle.Height/2);
+ block.Dispose();
+ centreLine.Dispose();
+
+ /*sliderRectangle.X = (this.Width - SliderWidth) / 2;
+ sliderRectangle.Width = SliderWidth;
+ sliderRectangle.Y = (int)((this.Height - SliderHeight) * percent);
+ sliderRectangle.Height = SliderHeight;
+ g.DrawImage(Images.Fader1,sliderRectangle);*/
+
+
+ }
+
+
+ ///
+ ///
+ ///
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ Graphics g = e.Graphics;
+ if(this.Orientation == Orientation.Vertical)
+ {
+ Brush groove = new SolidBrush(Color.Black);
+ g.FillRectangle(groove, this.Width / 2, SliderHeight / 2, 2, this.Height - SliderHeight);
+ groove.Dispose();
+ DrawSlider(g);
+ }
+
+ base.OnPaint (e);
+ }
+
+ private bool dragging;
+ private int dragY;
+
+ ///
+ ///
+ ///
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ if(sliderRectangle.Contains(e.X,e.Y))
+ {
+ dragging = true;
+ dragY = e.Y - sliderRectangle.Y;
+ }
+ // TODO: are we over the fader
+ base.OnMouseDown (e);
+ }
+
+ ///
+ ///
+ ///
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ if(dragging)
+ {
+ int sliderTop = e.Y - dragY;
+ if(sliderTop < 0)
+ {
+ this.percent = 0;
+ }
+ else if(sliderTop > this.Height - SliderHeight)
+ {
+ this.percent = 1;
+ }
+ else
+ {
+ percent = (float) sliderTop / (float) (this.Height - SliderHeight);
+ }
+ this.Invalidate();
+ }
+ base.OnMouseMove (e);
+ }
+
+ ///
+ ///
+ ///
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ dragging = false;
+ base.OnMouseUp (e);
+ }
+
+
+
+ ///
+ /// Minimum value of this fader
+ ///
+ public int Minimum
+ {
+ get
+ {
+ return minimum;
+ }
+ set
+ {
+ minimum = value;
+ }
+ }
+
+ ///
+ /// Maximum value of this fader
+ ///
+ public int Maximum
+ {
+ get
+ {
+ return maximum;
+ }
+ set
+ {
+ maximum = value;
+ }
+ }
+
+ ///
+ /// Current value of this fader
+ ///
+ public int Value
+ {
+ get
+ {
+ return (int) (percent * (maximum-minimum)) + minimum;
+ }
+ set
+ {
+ percent = (float) (value-minimum) / (maximum-minimum);
+ }
+ }
+
+ ///
+ /// Fader orientation
+ ///
+ public Orientation Orientation
+ {
+ get
+ {
+ return orientation;
+ }
+ set
+ {
+ orientation = value;
+ }
+ }
+
+ #region Component Designer generated code
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ }
+ #endregion
+ }
+}
diff --git a/NAudio/Gui/Fader.resx b/NAudio/Gui/Fader.resx
new file mode 100644
index 00000000..dd0ea4d8
--- /dev/null
+++ b/NAudio/Gui/Fader.resx
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.0.0.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
diff --git a/NAudio/Gui/PanSlider.cs b/NAudio/Gui/PanSlider.cs
new file mode 100644
index 00000000..5a6e23ef
--- /dev/null
+++ b/NAudio/Gui/PanSlider.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+
+namespace NAudio.Gui
+{
+ ///
+ /// Pan slider control
+ ///
+ public class PanSlider : System.Windows.Forms.UserControl
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.Container components = null;
+ private float pan;
+
+ ///
+ /// True when pan value changed
+ ///
+ public event EventHandler PanChanged;
+
+ ///
+ /// Creates a new PanSlider control
+ ///
+ public PanSlider()
+ {
+ // This call is required by the Windows.Forms Form Designer.
+ InitializeComponent();
+
+ // TODO: Add any initialization after the InitComponent call
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if( components != null )
+ components.Dispose();
+ }
+ base.Dispose( disposing );
+ }
+
+ #region Component Designer generated code
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ //
+ // PanSlider
+ //
+ this.Name = "PanSlider";
+ this.Size = new System.Drawing.Size(104, 16);
+
+ }
+ #endregion
+
+ ///
+ ///
+ ///
+ protected override void OnPaint(PaintEventArgs pe)
+ {
+ StringFormat format = new StringFormat();
+ format.LineAlignment = StringAlignment.Center;
+ format.Alignment = StringAlignment.Center;
+ string panValue;
+ if(pan == 0.0)
+ {
+ pe.Graphics.FillRectangle(Brushes.Orange,(this.Width/2) - 1 ,1,3,this.Height-2);
+ panValue = "C";
+ }
+ else if(pan > 0)
+ {
+ pe.Graphics.FillRectangle(Brushes.Orange,(this.Width/2),1,(int) ((this.Width/2) * pan),this.Height-2);
+ panValue = String.Format("{0:F0}%R",pan*100);
+ }
+ else
+ {
+ pe.Graphics.FillRectangle(Brushes.Orange,(int)((this.Width/2) * (pan+1)),1,(int) ((this.Width/2) * (0-pan)),this.Height-2);
+ panValue = String.Format("{0:F0}%L",pan*-100);
+ }
+ pe.Graphics.DrawRectangle(Pens.Black,0,0,this.Width-1,this.Height-1);
+
+ pe.Graphics.DrawString(panValue,this.Font,
+ Brushes.Black,this.ClientRectangle,format);
+ // Calling the base class OnPaint
+ //base.OnPaint(pe);
+ }
+
+ ///
+ ///
+ ///
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ if(e.Button == MouseButtons.Left)
+ {
+ SetPanFromMouse(e.X);
+ }
+ base.OnMouseMove (e);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ SetPanFromMouse(e.X);
+ base.OnMouseDown (e);
+ }
+
+ private void SetPanFromMouse(int x)
+ {
+ Pan = (((float) x / this.Width) * 2.0f) - 1.0f;
+ }
+
+ ///
+ /// The current Pan setting
+ ///
+ public float Pan
+ {
+ get
+ {
+ return pan;
+ }
+ set
+ {
+ if(value < -1.0f)
+ value = -1.0f;
+ if(value > 1.0f)
+ value = 1.0f;
+ if(value != pan)
+ {
+ pan = value;
+ if(PanChanged != null)
+ PanChanged(this,EventArgs.Empty);
+ Invalidate();
+ }
+
+ }
+ }
+ }
+}
diff --git a/NAudio/Gui/PanSlider.resx b/NAudio/Gui/PanSlider.resx
new file mode 100644
index 00000000..7b83e629
--- /dev/null
+++ b/NAudio/Gui/PanSlider.resx
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ 80
+
+
+ (Default)
+
+
+ False
+
+
+ PanSlider
+
+
+ Private
+
+
+ 8, 8
+
+
\ No newline at end of file
diff --git a/NAudio/Gui/Pot.Designer.cs b/NAudio/Gui/Pot.Designer.cs
new file mode 100644
index 00000000..96b0d489
--- /dev/null
+++ b/NAudio/Gui/Pot.Designer.cs
@@ -0,0 +1,45 @@
+namespace NAudio.Gui
+{
+ partial class Pot
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.SuspendLayout();
+ //
+ // Pot
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Name = "Pot";
+ this.Size = new System.Drawing.Size(32, 32);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Gui/Pot.cs b/NAudio/Gui/Pot.cs
new file mode 100644
index 00000000..4185f678
--- /dev/null
+++ b/NAudio/Gui/Pot.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Text;
+using System.Windows.Forms;
+
+namespace NAudio.Gui
+{
+ ///
+ /// Control that represents a potentiometer
+ /// TODO list:
+ /// Optional Log scale
+ /// Optional reverse scale
+ /// Keyboard control
+ /// Optional bitmap mode
+ /// Optional complete draw mode
+ /// Tooltip support
+ ///
+ public partial class Pot : UserControl
+ {
+ // control properties
+ private double minimum = 0.0;
+ private double maximum = 1.0;
+ private double value = 0.5;
+ //
+ private int beginDragY;
+ private double beginDragValue;
+ private bool dragging;
+
+ ///
+ /// Value changed event
+ ///
+ public event EventHandler ValueChanged;
+
+ ///
+ /// Creates a new pot control
+ ///
+ public Pot()
+ {
+ this.SetStyle(ControlStyles.DoubleBuffer |
+ ControlStyles.AllPaintingInWmPaint |
+ ControlStyles.UserPaint, true);
+ InitializeComponent();
+ }
+
+ ///
+ /// Minimum Value of the Pot
+ ///
+ public double Minimum
+ {
+ get
+ {
+ return minimum;
+ }
+ set
+ {
+ if (value >= maximum)
+ throw new ArgumentOutOfRangeException("Minimum must be less than maximum");
+ minimum = value;
+ if (Value < minimum)
+ Value = minimum;
+ }
+ }
+
+ ///
+ /// Maximum Value of the Pot
+ ///
+ public double Maximum
+ {
+ get
+ {
+ return maximum;
+ }
+ set
+ {
+ if (value <= minimum)
+ throw new ArgumentOutOfRangeException("Maximum must be greater than minimum");
+ maximum = value;
+ if (Value > maximum)
+ Value = maximum;
+ }
+ }
+
+ ///
+ /// The current value of the pot
+ ///
+ public double Value
+ {
+ get
+ {
+ return value;
+ }
+ set
+ {
+ SetValue(value, false);
+ }
+ }
+
+ private void SetValue(double newValue, bool raiseEvents)
+ {
+ if (this.value != newValue)
+ {
+ this.value = newValue;
+ if (raiseEvents)
+ {
+ if (ValueChanged != null)
+ ValueChanged(this, EventArgs.Empty);
+ }
+ Invalidate();
+ }
+ }
+
+ ///
+ /// Draws the control
+ ///
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ int diameter = Math.Min(this.Width-4,this.Height-4);
+
+ Pen potPen = new Pen(ForeColor,3.0f);
+ potPen.LineJoin = System.Drawing.Drawing2D.LineJoin.Round;
+ System.Drawing.Drawing2D.GraphicsState state = e.Graphics.Save();
+ //e.Graphics.TranslateTransform(diameter / 2f, diameter / 2f);
+ e.Graphics.TranslateTransform(this.Width / 2, this.Height / 2);
+ e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
+ e.Graphics.DrawArc(potPen, new Rectangle(diameter / -2, diameter / -2, diameter, diameter), 135, 270);
+
+ double percent = (value - minimum) / (maximum - minimum);
+ double degrees = 135 + (percent * 270);
+ double x = (diameter / 2.0) * Math.Cos(Math.PI * degrees / 180);
+ double y = (diameter / 2.0) * Math.Sin(Math.PI * degrees / 180);
+ e.Graphics.DrawLine(potPen, 0, 0, (float) x, (float) y);
+ e.Graphics.Restore(state);
+ base.OnPaint(e);
+ }
+
+ ///
+ /// Handles the mouse down event to allow changing value by dragging
+ ///
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ dragging = true;
+ beginDragY = e.Y;
+ beginDragValue = value;
+ base.OnMouseDown(e);
+ }
+
+ ///
+ /// Handles the mouse up event to allow changing value by dragging
+ ///
+ protected override void OnMouseUp(MouseEventArgs e)
+ {
+ dragging = false;
+ base.OnMouseUp(e);
+ }
+
+ ///
+ /// Handles the mouse down event to allow changing value by dragging
+ ///
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ if (dragging)
+ {
+ int yDifference = beginDragY - e.Y;
+ // 100 is the number of pixels of vertical movement that represents the whole scale
+ double delta = (maximum - minimum) * (yDifference / 150.0);
+ double newValue = beginDragValue + delta;
+ if (newValue < minimum)
+ newValue = minimum;
+ if (newValue > maximum)
+ newValue = maximum;
+ SetValue(newValue,true);
+ }
+ base.OnMouseMove(e);
+ }
+ }
+
+
+}
diff --git a/NAudio/Gui/Pot.resx b/NAudio/Gui/Pot.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/NAudio/Gui/Pot.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudio/Gui/VolumeMeter.Designer.cs b/NAudio/Gui/VolumeMeter.Designer.cs
new file mode 100644
index 00000000..9dff7cd0
--- /dev/null
+++ b/NAudio/Gui/VolumeMeter.Designer.cs
@@ -0,0 +1,36 @@
+namespace NAudio.Gui
+{
+ partial class VolumeMeter
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Gui/VolumeMeter.cs b/NAudio/Gui/VolumeMeter.cs
new file mode 100644
index 00000000..cb0c1ab0
--- /dev/null
+++ b/NAudio/Gui/VolumeMeter.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+
+namespace NAudio.Gui
+{
+ ///
+ /// Implements a rudimentary volume meter
+ ///
+ public partial class VolumeMeter : Control
+ {
+ Brush foregroundBrush;
+
+ ///
+ /// Basic volume meter
+ ///
+ public VolumeMeter()
+ {
+ this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
+ ControlStyles.OptimizedDoubleBuffer, true);
+ MinDb = -60;
+ MaxDb = 18;
+ Amplitude = 0;
+ Orientation = Orientation.Vertical;
+ InitializeComponent();
+ OnForeColorChanged(EventArgs.Empty);
+ }
+
+ ///
+ /// On Fore Color Changed
+ ///
+ protected override void OnForeColorChanged(EventArgs e)
+ {
+ foregroundBrush = new SolidBrush(ForeColor);
+ base.OnForeColorChanged(e);
+ }
+
+ private float amplitude;
+
+ ///
+ /// Current Value
+ ///
+ [DefaultValue(-3.0)]
+ public float Amplitude
+ {
+ get { return amplitude; }
+ set
+ {
+ amplitude = value;
+ this.Invalidate();
+ }
+ }
+
+ ///
+ /// Minimum decibels
+ ///
+ [DefaultValue(-60.0)]
+ public float MinDb { get; set; }
+
+ ///
+ /// Maximum decibels
+ ///
+ [DefaultValue(18.0)]
+ public float MaxDb { get; set; }
+
+ ///
+ /// Meter orientation
+ ///
+ [DefaultValue(Orientation.Vertical)]
+ public Orientation Orientation { get; set; }
+
+ ///
+ /// Paints the volume meter
+ ///
+ protected override void OnPaint(PaintEventArgs pe)
+ {
+ //base.OnPaint(pe);
+
+
+ pe.Graphics.DrawRectangle(Pens.Black, 0, 0, this.Width - 1, this.Height - 1);
+
+ double db = 20 * Math.Log10(Amplitude);
+ if (db < MinDb)
+ db = MinDb;
+ if (db > MaxDb)
+ db = MaxDb;
+ double percent = (db - MinDb) / (MaxDb - MinDb);
+
+ int width = this.Width - 2;
+ int height = this.Height - 2;
+ if (Orientation == Orientation.Horizontal)
+ {
+ width = (int)(width * percent);
+
+ pe.Graphics.FillRectangle(foregroundBrush, 1, 1, width, height);
+ }
+ else
+ {
+ height = (int)(height * percent);
+ pe.Graphics.FillRectangle(foregroundBrush, 1, this.Height - 1 - height, width, height);
+ }
+
+ /*
+ StringFormat format = new StringFormat();
+ format.LineAlignment = StringAlignment.Center;
+ format.Alignment = StringAlignment.Center;
+ string dbValue = String.Format("{0:F2} dB", db);
+ if(Double.IsNegativeInfinity(db))
+ {
+ dbValue = "-\x221e db"; // -8 dB
+ }
+
+ pe.Graphics.DrawString(dbValue, this.Font,
+ Brushes.Black, this.ClientRectangle, format);*/
+
+ }
+ }
+}
diff --git a/NAudio/Gui/VolumeSlider.cs b/NAudio/Gui/VolumeSlider.cs
new file mode 100644
index 00000000..d2b7f0dd
--- /dev/null
+++ b/NAudio/Gui/VolumeSlider.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+
+namespace NAudio.Gui
+{
+ ///
+ /// VolumeSlider control
+ ///
+ public class VolumeSlider : System.Windows.Forms.UserControl
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.Container components = null;
+ private float volume = 1.0f;
+ private float MinDb = -48;
+ ///
+ /// Volume changed event
+ ///
+ public event EventHandler VolumeChanged;
+
+ ///
+ /// Creates a new VolumeSlider control
+ ///
+ public VolumeSlider()
+ {
+ // This call is required by the Windows.Forms Form Designer.
+ InitializeComponent();
+
+ // TODO: Add any initialization after the InitComponent call
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (components != null)
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ //
+ // VolumeSlider
+ //
+ this.Name = "VolumeSlider";
+ this.Size = new System.Drawing.Size(96, 16);
+
+ }
+ #endregion
+
+ ///
+ ///
+ ///
+ protected override void OnPaint(PaintEventArgs pe)
+ {
+ StringFormat format = new StringFormat();
+ format.LineAlignment = StringAlignment.Center;
+ format.Alignment = StringAlignment.Center;
+
+ pe.Graphics.DrawRectangle(Pens.Black, 0, 0, this.Width - 1, this.Height - 1);
+ float db = 20 * (float)Math.Log10(Volume);
+ float percent = 1 - (db / MinDb);
+
+ pe.Graphics.FillRectangle(Brushes.LightGreen, 1, 1, (int)((this.Width - 2) * percent), this.Height - 2);
+ string dbValue = String.Format("{0:F2} dB", db);
+ /*if(Double.IsNegativeInfinity(db))
+ {
+ dbValue = "-\x221e db"; // -8 dB
+ }*/
+
+ pe.Graphics.DrawString(dbValue, this.Font,
+ Brushes.Black, this.ClientRectangle, format);
+ // Calling the base class OnPaint
+ //base.OnPaint(pe);
+ }
+
+ ///
+ ///
+ ///
+ protected override void OnMouseMove(MouseEventArgs e)
+ {
+ if (e.Button == MouseButtons.Left)
+ {
+ SetVolumeFromMouse(e.X);
+ }
+ base.OnMouseMove(e);
+ }
+
+ ///
+ ///
+ ///
+ protected override void OnMouseDown(MouseEventArgs e)
+ {
+ SetVolumeFromMouse(e.X);
+ base.OnMouseDown(e);
+ }
+
+ private void SetVolumeFromMouse(int x)
+ {
+
+ // linear Volume = (float) x / this.Width;
+ float dbVolume = (1 - (float)x / this.Width) * MinDb;
+ if (x <= 0)
+ Volume = 0;
+ else
+ Volume = (float)Math.Pow(10, dbVolume / 20);
+ }
+
+ ///
+ /// The volume for this control
+ ///
+ [DefaultValue(1.0f)]
+ public float Volume
+ {
+ get
+ {
+ return volume;
+ }
+ set
+ {
+ if (value < 0.0f)
+ value = 0.0f;
+ if (value > 1.0f)
+ value = 1.0f;
+ if (volume != value)
+ {
+ volume = value;
+ if (VolumeChanged != null)
+ VolumeChanged(this, EventArgs.Empty);
+ Invalidate();
+ }
+ }
+ }
+ }
+}
diff --git a/NAudio/Gui/VolumeSlider.resx b/NAudio/Gui/VolumeSlider.resx
new file mode 100644
index 00000000..7238458f
--- /dev/null
+++ b/NAudio/Gui/VolumeSlider.resx
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ True
+
+
+ 80
+
+
+ (Default)
+
+
+ False
+
+
+ Private
+
+
+ VolumeSlider
+
+
+ 8, 8
+
+
\ No newline at end of file
diff --git a/NAudio/Gui/WaveViewer.cs b/NAudio/Gui/WaveViewer.cs
new file mode 100644
index 00000000..ee4c206e
--- /dev/null
+++ b/NAudio/Gui/WaveViewer.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Windows.Forms;
+using NAudio.Wave;
+
+namespace NAudio.Gui
+{
+ ///
+ /// Control for viewing waveforms
+ ///
+ public class WaveViewer : System.Windows.Forms.UserControl
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.Container components = null;
+ private WaveStream waveStream;
+ private int samplesPerPixel = 128;
+ private long startPosition;
+ private int bytesPerSample;
+ ///
+ /// Creates a new WaveViewer control
+ ///
+ public WaveViewer()
+ {
+ // This call is required by the Windows.Forms Form Designer.
+ InitializeComponent();
+ this.DoubleBuffered = true;
+
+ }
+
+ ///
+ /// sets the associated wavestream
+ ///
+ public WaveStream WaveStream
+ {
+ get
+ {
+ return waveStream;
+ }
+ set
+ {
+ waveStream = value;
+ if (waveStream != null)
+ {
+ bytesPerSample = (waveStream.WaveFormat.BitsPerSample / 8) * waveStream.WaveFormat.Channels;
+ }
+ this.Invalidate();
+ }
+ }
+
+ ///
+ /// The zoom level, in samples per pixel
+ ///
+ public int SamplesPerPixel
+ {
+ get
+ {
+ return samplesPerPixel;
+ }
+ set
+ {
+ samplesPerPixel = value;
+ this.Invalidate();
+ }
+ }
+
+ ///
+ /// Start position (currently in bytes)
+ ///
+ public long StartPosition
+ {
+ get
+ {
+ return startPosition;
+ }
+ set
+ {
+ startPosition = value;
+ }
+ }
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ protected override void Dispose( bool disposing )
+ {
+ if( disposing )
+ {
+ if(components != null)
+ {
+ components.Dispose();
+ }
+ }
+ base.Dispose( disposing );
+ }
+
+ ///
+ ///
+ ///
+ protected override void OnPaint(PaintEventArgs e)
+ {
+ if(waveStream != null)
+ {
+ waveStream.Position = 0;
+ int bytesRead;
+ byte[] waveData = new byte[samplesPerPixel*bytesPerSample];
+ waveStream.Position = startPosition + (e.ClipRectangle.Left * bytesPerSample * samplesPerPixel);
+
+ for(float x = e.ClipRectangle.X; x < e.ClipRectangle.Right; x+=1)
+ {
+ short low = 0;
+ short high = 0;
+ bytesRead = waveStream.Read(waveData, 0, samplesPerPixel * bytesPerSample);
+ if(bytesRead == 0)
+ break;
+ for(int n = 0; n < bytesRead; n+=2)
+ {
+ short sample = BitConverter.ToInt16(waveData, n);
+ if(sample < low) low = sample;
+ if(sample > high) high = sample;
+ }
+ float lowPercent = ((((float) low) - short.MinValue) / ushort.MaxValue);
+ float highPercent = ((((float) high) - short.MinValue) / ushort.MaxValue);
+ e.Graphics.DrawLine(Pens.Black,x,this.Height * lowPercent,x,this.Height * highPercent);
+ }
+ }
+
+ base.OnPaint (e);
+ }
+
+
+ #region Component Designer generated code
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ }
+ #endregion
+ }
+}
diff --git a/NAudio/Gui/WaveViewer.resx b/NAudio/Gui/WaveViewer.resx
new file mode 100644
index 00000000..dd0ea4d8
--- /dev/null
+++ b/NAudio/Gui/WaveViewer.resx
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.0.0.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
diff --git a/NAudio/Gui/WaveformPainter.Designer.cs b/NAudio/Gui/WaveformPainter.Designer.cs
new file mode 100644
index 00000000..d4c7fb32
--- /dev/null
+++ b/NAudio/Gui/WaveformPainter.Designer.cs
@@ -0,0 +1,36 @@
+namespace NAudio.Gui
+{
+ partial class WaveformPainter
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Gui/WaveformPainter.cs b/NAudio/Gui/WaveformPainter.cs
new file mode 100644
index 00000000..c0c1be8c
--- /dev/null
+++ b/NAudio/Gui/WaveformPainter.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+
+namespace NAudio.Gui
+{
+ ///
+ /// Windows Forms control for painting audio waveforms
+ ///
+ public partial class WaveformPainter : Control
+ {
+ Pen foregroundPen;
+ List samples = new List(1000);
+ int maxSamples;
+ int insertPos;
+
+ ///
+ /// Constructs a new instance of the WaveFormPainter class
+ ///
+ public WaveformPainter()
+ {
+ this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
+ ControlStyles.OptimizedDoubleBuffer, true);
+ InitializeComponent();
+ OnForeColorChanged(EventArgs.Empty);
+ OnResize(EventArgs.Empty);
+ }
+
+ ///
+ /// On Resize
+ ///
+ protected override void OnResize(EventArgs e)
+ {
+ maxSamples = this.Width;
+ base.OnResize(e);
+ }
+
+ ///
+ /// On ForeColor Changed
+ ///
+ ///
+ protected override void OnForeColorChanged(EventArgs e)
+ {
+ foregroundPen = new Pen(ForeColor);
+ base.OnForeColorChanged(e);
+ }
+
+ ///
+ /// Add Max Value
+ ///
+ ///
+ public void AddMax(float maxSample)
+ {
+ if (maxSamples == 0)
+ {
+ // sometimes when you minimise, max samples can be set to 0
+ return;
+ }
+ if (samples.Count <= maxSamples)
+ {
+ samples.Add(maxSample);
+ }
+ else if (insertPos < maxSamples)
+ {
+ samples[insertPos] = maxSample;
+ }
+ insertPos++;
+ insertPos %= maxSamples;
+
+ this.Invalidate();
+ }
+
+ ///
+ /// On Paint
+ ///
+ protected override void OnPaint(PaintEventArgs pe)
+ {
+ base.OnPaint(pe);
+
+ for (int x = 0; x < this.Width; x++)
+ {
+ float lineHeight = this.Height * GetSample(x - this.Width + insertPos);
+ float y1 = (this.Height - lineHeight) / 2;
+ pe.Graphics.DrawLine(foregroundPen, x, y1, x, y1 + lineHeight);
+ }
+ }
+
+ float GetSample(int index)
+ {
+ if (index < 0)
+ index += maxSamples;
+ if (index >= 0 & index < samples.Count)
+ return samples[index];
+ return 0;
+ }
+ }
+}
diff --git a/NAudio/MediaFoundation/AudioSubtypes.cs b/NAudio/MediaFoundation/AudioSubtypes.cs
new file mode 100644
index 00000000..b6ba751d
--- /dev/null
+++ b/NAudio/MediaFoundation/AudioSubtypes.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Audio Subtype GUIDs
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa372553%28v=vs.85%29.aspx
+ ///
+ public static class AudioSubtypes
+ {
+ ///
+ /// Advanced Audio Coding (AAC).
+ ///
+ [FieldDescription("AAC")]
+ public static readonly Guid MFAudioFormat_AAC = new Guid("00001610-0000-0010-8000-00aa00389b71");
+ ///
+ /// Not used
+ ///
+ [FieldDescription("ADTS")]
+ public static readonly Guid MFAudioFormat_ADTS = new Guid("00001600-0000-0010-8000-00aa00389b71");
+ ///
+ /// Dolby AC-3 audio over Sony/Philips Digital Interface (S/PDIF).
+ ///
+ [FieldDescription("Dolby AC3 SPDIF")]
+ public static readonly Guid MFAudioFormat_Dolby_AC3_SPDIF = new Guid("00000092-0000-0010-8000-00aa00389b71");
+ ///
+ /// Encrypted audio data used with secure audio path.
+ ///
+ [FieldDescription("DRM")]
+ public static readonly Guid MFAudioFormat_DRM = new Guid("00000009-0000-0010-8000-00aa00389b71");
+ ///
+ /// Digital Theater Systems (DTS) audio.
+ ///
+ [FieldDescription("DTS")]
+ public static readonly Guid MFAudioFormat_DTS = new Guid("00000008-0000-0010-8000-00aa00389b71");
+ ///
+ /// Uncompressed IEEE floating-point audio.
+ ///
+ [FieldDescription("IEEE floating-point")]
+ public static readonly Guid MFAudioFormat_Float = new Guid("00000003-0000-0010-8000-00aa00389b71");
+ ///
+ /// MPEG Audio Layer-3 (MP3).
+ ///
+ [FieldDescription("MP3")]
+ public static readonly Guid MFAudioFormat_MP3 = new Guid("00000055-0000-0010-8000-00aa00389b71");
+ ///
+ /// MPEG-1 audio payload.
+ ///
+ [FieldDescription("MPEG")]
+ public static readonly Guid MFAudioFormat_MPEG = new Guid("00000050-0000-0010-8000-00aa00389b71");
+ ///
+ /// Windows Media Audio 9 Voice codec.
+ ///
+ [FieldDescription("WMA 9 Voice codec")]
+ public static readonly Guid MFAudioFormat_MSP1 = new Guid("0000000a-0000-0010-8000-00aa00389b71");
+ ///
+ /// Uncompressed PCM audio.
+ ///
+ [FieldDescription("PCM")]
+ public static readonly Guid MFAudioFormat_PCM = new Guid("00000001-0000-0010-8000-00aa00389b71");
+ ///
+ /// Windows Media Audio 9 Professional codec over S/PDIF.
+ ///
+ [FieldDescription("WMA SPDIF")]
+ public static readonly Guid MFAudioFormat_WMASPDIF = new Guid("00000164-0000-0010-8000-00aa00389b71");
+ ///
+ /// Windows Media Audio 9 Lossless codec or Windows Media Audio 9.1 codec.
+ ///
+ [FieldDescription("WMAudio Lossless")]
+ public static readonly Guid MFAudioFormat_WMAudio_Lossless = new Guid("00000163-0000-0010-8000-00aa00389b71");
+ ///
+ /// Windows Media Audio 8 codec, Windows Media Audio 9 codec, or Windows Media Audio 9.1 codec.
+ ///
+ [FieldDescription("Windows Media Audio")]
+ public static readonly Guid MFAudioFormat_WMAudioV8 = new Guid("00000161-0000-0010-8000-00aa00389b71");
+ ///
+ /// Windows Media Audio 9 Professional codec or Windows Media Audio 9.1 Professional codec.
+ ///
+ [FieldDescription("Windows Media Audio Professional")]
+ public static readonly Guid MFAudioFormat_WMAudioV9 = new Guid("00000162-0000-0010-8000-00aa00389b71");
+ ///
+ /// Dolby Digital (AC-3).
+ ///
+ [FieldDescription("Dolby AC3")]
+ public static readonly Guid MFAudioFormat_Dolby_AC3 = new Guid("e06d802c-db46-11cf-b4d1-00805f6cbbea");
+
+ ///
+ /// MPEG-4 and AAC Audio Types
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd317599(v=vs.85).aspx
+ /// Reference : wmcodecdsp.h
+ ///
+ [FieldDescription("MPEG-4 and AAC Audio Types")]
+ public static readonly Guid MEDIASUBTYPE_RAW_AAC1 = new Guid("000000ff-0000-0010-8000-00aa00389b71");
+
+ ///
+ /// Dolby Audio Types
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd317599(v=vs.85).aspx
+ /// Reference : wmcodecdsp.h
+ ///
+ [FieldDescription("Dolby Audio Types")]
+ public static readonly Guid MEDIASUBTYPE_DVM = new Guid("00002000-0000-0010-8000-00aa00389b71");
+
+ ///
+ /// Dolby Audio Types
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd317599(v=vs.85).aspx
+ /// Reference : wmcodecdsp.h
+ ///
+ [FieldDescription("Dolby Audio Types")]
+ public static readonly Guid MEDIASUBTYPE_DOLBY_DDPLUS = new Guid("a7fb87af-2d02-42fb-a4d4-05cd93843bdd");
+
+ ///
+ /// μ-law coding
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd390971(v=vs.85).aspx
+ /// Reference : Ksmedia.h
+ ///
+ [FieldDescription("μ-law")]
+ public static readonly Guid KSDATAFORMAT_SUBTYPE_MULAW = new Guid("00000007-0000-0010-8000-00aa00389b71");
+
+ ///
+ /// Adaptive delta pulse code modulation (ADPCM)
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd390971(v=vs.85).aspx
+ /// Reference : Ksmedia.h
+ ///
+ [FieldDescription("ADPCM")]
+ public static readonly Guid KSDATAFORMAT_SUBTYPE_ADPCM = new Guid("00000002-0000-0010-8000-00aa00389b71");
+
+ ///
+ /// Dolby Digital Plus formatted for HDMI output.
+ /// http://msdn.microsoft.com/en-us/library/windows/hardware/ff538392(v=vs.85).aspx
+ /// Reference : internet
+ ///
+ [FieldDescription("Dolby Digital Plus for HDMI")]
+ public static readonly Guid KSDATAFORMAT_SUBTYPE_IEC61937_DOLBY_DIGITAL_PLUS = new Guid("0000000a-0cea-0010-8000-00aa00389b71");
+
+ ///
+ /// MSAudio1 - unknown meaning
+ /// Reference : wmcodecdsp.h
+ ///
+ [FieldDescription("MSAudio1")]
+ public static readonly Guid MEDIASUBTYPE_MSAUDIO1 = new Guid("00000160-0000-0010-8000-00aa00389b71");
+
+ ///
+ /// IMA ADPCM ACM Wrapper
+ ///
+ [FieldDescription("IMA ADPCM")]
+ public static readonly Guid ImaAdpcm = new Guid("00000011-0000-0010-8000-00aa00389b71");
+
+ ///
+ /// WMSP2 - unknown meaning
+ /// Reference: wmsdkidl.h
+ ///
+ [FieldDescription("WMSP2")]
+ public static readonly Guid WMMEDIASUBTYPE_WMSP2 = new Guid("0000000b-0000-0010-8000-00aa00389b71");
+
+
+ // TODO: find out what these are, and add them:
+ // {00000031-0000-0010-8000-00aa00389b71} // probably GSM610 ACM wrapper
+
+ }
+}
diff --git a/NAudio/MediaFoundation/IMFActivate.cs b/NAudio/MediaFoundation/IMFActivate.cs
new file mode 100644
index 00000000..8d3b03cd
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFActivate.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// IMFActivate, defined in mfobjects.h
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("7FEE9E9A-4A89-47a6-899C-B6A53A70FB67")]
+ public interface IMFActivate : IMFAttributes
+ {
+ ///
+ /// Retrieves the value associated with a key.
+ ///
+ new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Retrieves the data type of the value associated with a key.
+ ///
+ new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pType);
+
+ ///
+ /// Queries whether a stored attribute value equals a specified PROPVARIANT.
+ ///
+ new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Compares the attributes on this object with the attributes on another object.
+ ///
+ new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Retrieves a UINT32 value associated with a key.
+ ///
+ new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);
+
+ ///
+ /// Retrieves a UINT64 value associated with a key.
+ ///
+ new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);
+
+ ///
+ /// Retrieves a double value associated with a key.
+ ///
+ new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);
+
+ ///
+ /// Retrieves a GUID value associated with a key.
+ ///
+ new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);
+
+ ///
+ /// Retrieves the length of a string value associated with a key.
+ ///
+ new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key.
+ ///
+ new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize,
+ out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key. This method allocates the memory for the string.
+ ///
+ new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue,
+ out int pcchLength);
+
+ ///
+ /// Retrieves the length of a byte array associated with a key.
+ ///
+ new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key.
+ ///
+ new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize,
+ out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key. This method allocates the memory for the array.
+ ///
+ new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);
+
+ ///
+ /// Retrieves an interface pointer associated with a key.
+ ///
+ new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
+ [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
+
+ ///
+ /// Associates an attribute value with a key.
+ ///
+ new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);
+
+ ///
+ /// Removes a key/value pair from the object's attribute list.
+ ///
+ new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);
+
+ ///
+ /// Removes all key/value pairs from the object's attribute list.
+ ///
+ new void DeleteAllItems();
+
+ ///
+ /// Associates a UINT32 value with a key.
+ ///
+ new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);
+
+ ///
+ /// Associates a UINT64 value with a key.
+ ///
+ new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);
+
+ ///
+ /// Associates a double value with a key.
+ ///
+ new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);
+
+ ///
+ /// Associates a GUID value with a key.
+ ///
+ new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);
+
+ ///
+ /// Associates a wide-character string with a key.
+ ///
+ new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);
+
+ ///
+ /// Associates a byte array with a key.
+ ///
+ new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf,
+ int cbBufSize);
+
+ ///
+ /// Associates an IUnknown pointer with a key.
+ ///
+ new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
+
+ ///
+ /// Locks the attribute store so that no other thread can access it.
+ ///
+ new void LockStore();
+
+ ///
+ /// Unlocks the attribute store.
+ ///
+ new void UnlockStore();
+
+ ///
+ /// Retrieves the number of attributes that are set on this object.
+ ///
+ new void GetCount(out int pcItems);
+
+ ///
+ /// Retrieves an attribute at the specified index.
+ ///
+ new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Copies all of the attributes from this object into another attribute store.
+ ///
+ new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);
+
+ ///
+ /// Creates the object associated with this activation object.
+ ///
+ void ActivateObject([In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out,MarshalAs(UnmanagedType.Interface)] out object ppv);
+
+ ///
+ /// Shuts down the created object.
+ ///
+ void ShutdownObject();
+
+ ///
+ /// Detaches the created object from the activation object.
+ ///
+ void DetachObject();
+ }
+}
diff --git a/NAudio/MediaFoundation/IMFAttributes.cs b/NAudio/MediaFoundation/IMFAttributes.cs
new file mode 100644
index 00000000..56aab061
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFAttributes.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Provides a generic way to store key/value pairs on an object.
+ /// http://msdn.microsoft.com/en-gb/library/windows/desktop/ms704598%28v=vs.85%29.aspx
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("2CD2D921-C447-44A7-A13C-4ADABFC247E3")]
+ public interface IMFAttributes
+ {
+ ///
+ /// Retrieves the value associated with a key.
+ ///
+ void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Retrieves the data type of the value associated with a key.
+ ///
+ void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pType);
+
+ ///
+ /// Queries whether a stored attribute value equals a specified PROPVARIANT.
+ ///
+ void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Compares the attributes on this object with the attributes on another object.
+ ///
+ void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Retrieves a UINT32 value associated with a key.
+ ///
+ void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);
+
+ ///
+ /// Retrieves a UINT64 value associated with a key.
+ ///
+ void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);
+
+ ///
+ /// Retrieves a double value associated with a key.
+ ///
+ void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);
+
+ ///
+ /// Retrieves a GUID value associated with a key.
+ ///
+ void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);
+
+ ///
+ /// Retrieves the length of a string value associated with a key.
+ ///
+ void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key.
+ ///
+ void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize,
+ out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key. This method allocates the memory for the string.
+ ///
+ void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue,
+ out int pcchLength);
+
+ ///
+ /// Retrieves the length of a byte array associated with a key.
+ ///
+ void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key.
+ ///
+ void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize,
+ out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key. This method allocates the memory for the array.
+ ///
+ void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);
+
+ ///
+ /// Retrieves an interface pointer associated with a key.
+ ///
+ void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
+ [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
+
+ ///
+ /// Associates an attribute value with a key.
+ ///
+ void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);
+
+ ///
+ /// Removes a key/value pair from the object's attribute list.
+ ///
+ void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);
+
+ ///
+ /// Removes all key/value pairs from the object's attribute list.
+ ///
+ void DeleteAllItems();
+
+ ///
+ /// Associates a UINT32 value with a key.
+ ///
+ void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);
+
+ ///
+ /// Associates a UINT64 value with a key.
+ ///
+ void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);
+
+ ///
+ /// Associates a double value with a key.
+ ///
+ void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);
+
+ ///
+ /// Associates a GUID value with a key.
+ ///
+ void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);
+
+ ///
+ /// Associates a wide-character string with a key.
+ ///
+ void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);
+
+ ///
+ /// Associates a byte array with a key.
+ ///
+ void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf,
+ int cbBufSize);
+
+ ///
+ /// Associates an IUnknown pointer with a key.
+ ///
+ void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
+
+ ///
+ /// Locks the attribute store so that no other thread can access it.
+ ///
+ void LockStore();
+
+ ///
+ /// Unlocks the attribute store.
+ ///
+ void UnlockStore();
+
+ ///
+ /// Retrieves the number of attributes that are set on this object.
+ ///
+ void GetCount(out int pcItems);
+
+ ///
+ /// Retrieves an attribute at the specified index.
+ ///
+ void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Copies all of the attributes from this object into another attribute store.
+ ///
+ void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/IMFByteStream.cs b/NAudio/MediaFoundation/IMFByteStream.cs
new file mode 100644
index 00000000..4551ec57
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFByteStream.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// IMFByteStream
+ /// http://msdn.microsoft.com/en-gb/library/windows/desktop/ms698720%28v=vs.85%29.aspx
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("ad4c1b00-4bf7-422f-9175-756693d9130d")]
+ public interface IMFByteStream
+ {
+ ///
+ /// Retrieves the characteristics of the byte stream.
+ /// virtual HRESULT STDMETHODCALLTYPE GetCapabilities(/*[out]*/ __RPC__out DWORD *pdwCapabilities) = 0;
+ ///
+ void GetCapabilities(ref int pdwCapabiities);
+
+ ///
+ /// Retrieves the length of the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE GetLength(/*[out]*/ __RPC__out QWORD *pqwLength) = 0;
+ ///
+ void GetLength(ref long pqwLength);
+
+ ///
+ /// Sets the length of the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE SetLength(/*[in]*/ QWORD qwLength) = 0;
+ ///
+ void SetLength(long qwLength);
+
+ ///
+ /// Retrieves the current read or write position in the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE GetCurrentPosition(/*[out]*/ __RPC__out QWORD *pqwPosition) = 0;
+ ///
+ void GetCurrentPosition(ref long pqwPosition);
+
+ ///
+ /// Sets the current read or write position.
+ /// virtual HRESULT STDMETHODCALLTYPE SetCurrentPosition(/*[in]*/ QWORD qwPosition) = 0;
+ ///
+ void SetCurrentPosition(long qwPosition);
+
+ ///
+ /// Queries whether the current position has reached the end of the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE IsEndOfStream(/*[out]*/ __RPC__out BOOL *pfEndOfStream) = 0;
+ ///
+ void IsEndOfStream([MarshalAs(UnmanagedType.Bool)] ref bool pfEndOfStream);
+
+ ///
+ /// Reads data from the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE Read(/*[size_is][out]*/ __RPC__out_ecount_full(cb) BYTE *pb, /*[in]*/ ULONG cb, /*[out]*/ __RPC__out ULONG *pcbRead) = 0;
+ ///
+ void Read(IntPtr pb, int cb, ref int pcbRead);
+
+ ///
+ /// Begins an asynchronous read operation from the stream.
+ /// virtual /*[local]*/ HRESULT STDMETHODCALLTYPE BeginRead(/*[out]*/ _Out_writes_bytes_(cb) BYTE *pb, /*[in]*/ ULONG cb, /*[in]*/ IMFAsyncCallback *pCallback, /*[in]*/ IUnknown *punkState) = 0;
+ ///
+ void BeginRead(IntPtr pb, int cb, IntPtr pCallback, IntPtr punkState);
+
+ ///
+ /// Completes an asynchronous read operation.
+ /// virtual /*[local]*/ HRESULT STDMETHODCALLTYPE EndRead(/*[in]*/ IMFAsyncResult *pResult, /*[out]*/ _Out_ ULONG *pcbRead) = 0;
+ ///
+ void EndRead(IntPtr pResult, ref int pcbRead);
+
+ ///
+ /// Writes data to the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE Write(/*[size_is][in]*/ __RPC__in_ecount_full(cb) const BYTE *pb, /*[in]*/ ULONG cb, /*[out]*/ __RPC__out ULONG *pcbWritten) = 0;
+ ///
+ void Write(IntPtr pb, int cb, ref int pcbWritten);
+
+ ///
+ /// Begins an asynchronous write operation to the stream.
+ /// virtual /*[local]*/ HRESULT STDMETHODCALLTYPE BeginWrite(/*[in]*/ _In_reads_bytes_(cb) const BYTE *pb, /*[in]*/ ULONG cb, /*[in]*/ IMFAsyncCallback *pCallback, /*[in]*/ IUnknown *punkState) = 0;
+ ///
+ void BeginWrite(IntPtr pb, int cb, IntPtr pCallback, IntPtr punkState);
+
+ ///
+ /// Completes an asynchronous write operation.
+ /// virtual /*[local]*/ HRESULT STDMETHODCALLTYPE EndWrite(/*[in]*/ IMFAsyncResult *pResult, /*[out]*/ _Out_ ULONG *pcbWritten) = 0;
+ ///
+ void EndWrite(IntPtr pResult, ref int pcbWritten);
+
+ ///
+ /// Moves the current position in the stream by a specified offset.
+ /// virtual HRESULT STDMETHODCALLTYPE Seek(/*[in]*/ MFBYTESTREAM_SEEK_ORIGIN SeekOrigin, /*[in]*/ LONGLONG llSeekOffset, /*[in]*/ DWORD dwSeekFlags, /*[out]*/ __RPC__out QWORD *pqwCurrentPosition) = 0;
+ ///
+ void Seek(int SeekOrigin, long llSeekOffset, int dwSeekFlags, ref long pqwCurrentPosition);
+
+ ///
+ /// Clears any internal buffers used by the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE Flush( void) = 0;
+ ///
+ void Flush();
+
+ ///
+ /// Closes the stream and releases any resources associated with the stream.
+ /// virtual HRESULT STDMETHODCALLTYPE Close( void) = 0;
+ ///
+ void Close();
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/IMFCollection.cs b/NAudio/MediaFoundation/IMFCollection.cs
new file mode 100644
index 00000000..c800c6a0
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFCollection.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Represents a generic collection of IUnknown pointers.
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("5BC8A76B-869A-46A3-9B03-FA218A66AEBE")]
+ public interface IMFCollection
+ {
+ ///
+ /// Retrieves the number of objects in the collection.
+ ///
+ void GetElementCount(out int pcElements);
+
+ ///
+ /// Retrieves an object in the collection.
+ ///
+ void GetElement([In] int dwElementIndex, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppUnkElement);
+
+ ///
+ /// Adds an object to the collection.
+ ///
+ void AddElement([In, MarshalAs(UnmanagedType.IUnknown)] object pUnkElement);
+
+ ///
+ /// Removes an object from the collection.
+ ///
+ void RemoveElement([In] int dwElementIndex, [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppUnkElement);
+
+ ///
+ /// Removes an object from the collection.
+ ///
+ void InsertElementAt([In] int dwIndex, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
+
+ ///
+ /// Removes all items from the collection.
+ ///
+ void RemoveAllElements();
+ }
+}
diff --git a/NAudio/MediaFoundation/IMFMediaBuffer.cs b/NAudio/MediaFoundation/IMFMediaBuffer.cs
new file mode 100644
index 00000000..4f465b9f
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFMediaBuffer.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// IMFMediaBuffer
+ /// http://msdn.microsoft.com/en-gb/library/windows/desktop/ms696261%28v=vs.85%29.aspx
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("045FA593-8799-42b8-BC8D-8968C6453507")]
+ public interface IMFMediaBuffer
+ {
+ ///
+ /// Gives the caller access to the memory in the buffer.
+ ///
+ void Lock(out IntPtr ppbBuffer, out int pcbMaxLength, out int pcbCurrentLength);
+ ///
+ /// Unlocks a buffer that was previously locked.
+ ///
+ void Unlock();
+ ///
+ /// Retrieves the length of the valid data in the buffer.
+ ///
+ void GetCurrentLength(out int pcbCurrentLength);
+ ///
+ /// Sets the length of the valid data in the buffer.
+ ///
+ void SetCurrentLength(int cbCurrentLength);
+ ///
+ /// Retrieves the allocated size of the buffer.
+ ///
+ void GetMaxLength(out int pcbMaxLength);
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/IMFMediaEvent.cs b/NAudio/MediaFoundation/IMFMediaEvent.cs
new file mode 100644
index 00000000..45b8b17f
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFMediaEvent.cs
@@ -0,0 +1,208 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// IMFMediaEvent - Represents an event generated by a Media Foundation object. Use this interface to get information about the event.
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms702249%28v=vs.85%29.aspx
+ /// Mfobjects.h
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("DF598932-F10C-4E39-BBA2-C308F101DAA3")]
+ public interface IMFMediaEvent : IMFAttributes
+ {
+ ///
+ /// Retrieves the value associated with a key.
+ ///
+ new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Retrieves the data type of the value associated with a key.
+ ///
+ new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pType);
+
+ ///
+ /// Queries whether a stored attribute value equals a specified PROPVARIANT.
+ ///
+ new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Compares the attributes on this object with the attributes on another object.
+ ///
+ new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Retrieves a UINT32 value associated with a key.
+ ///
+ new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);
+
+ ///
+ /// Retrieves a UINT64 value associated with a key.
+ ///
+ new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);
+
+ ///
+ /// Retrieves a double value associated with a key.
+ ///
+ new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);
+
+ ///
+ /// Retrieves a GUID value associated with a key.
+ ///
+ new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);
+
+ ///
+ /// Retrieves the length of a string value associated with a key.
+ ///
+ new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key.
+ ///
+ new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize,
+ out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key. This method allocates the memory for the string.
+ ///
+ new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue,
+ out int pcchLength);
+
+ ///
+ /// Retrieves the length of a byte array associated with a key.
+ ///
+ new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key.
+ ///
+ new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize,
+ out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key. This method allocates the memory for the array.
+ ///
+ new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);
+
+ ///
+ /// Retrieves an interface pointer associated with a key.
+ ///
+ new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
+ [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
+
+ ///
+ /// Associates an attribute value with a key.
+ ///
+ new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);
+
+ ///
+ /// Removes a key/value pair from the object's attribute list.
+ ///
+ new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);
+
+ ///
+ /// Removes all key/value pairs from the object's attribute list.
+ ///
+ new void DeleteAllItems();
+
+ ///
+ /// Associates a UINT32 value with a key.
+ ///
+ new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);
+
+ ///
+ /// Associates a UINT64 value with a key.
+ ///
+ new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);
+
+ ///
+ /// Associates a double value with a key.
+ ///
+ new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);
+
+ ///
+ /// Associates a GUID value with a key.
+ ///
+ new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);
+
+ ///
+ /// Associates a wide-character string with a key.
+ ///
+ new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);
+
+ ///
+ /// Associates a byte array with a key.
+ ///
+ new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf,
+ int cbBufSize);
+
+ ///
+ /// Associates an IUnknown pointer with a key.
+ ///
+ new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
+
+ ///
+ /// Locks the attribute store so that no other thread can access it.
+ ///
+ new void LockStore();
+
+ ///
+ /// Unlocks the attribute store.
+ ///
+ new void UnlockStore();
+
+ ///
+ /// Retrieves the number of attributes that are set on this object.
+ ///
+ new void GetCount(out int pcItems);
+
+ ///
+ /// Retrieves an attribute at the specified index.
+ ///
+ new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Copies all of the attributes from this object into another attribute store.
+ ///
+ new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);
+
+ ///
+ /// Retrieves the event type.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetType(
+ /// /* [out] */ __RPC__out MediaEventType *pmet) = 0;
+ ///
+ void GetType([Out] out MediaEventType pmet);
+
+ ///
+ /// Retrieves the extended type of the event.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetExtendedType(
+ /// /* [out] */ __RPC__out GUID *pguidExtendedType) = 0;
+ ///
+ void GetExtendedType([Out] out Guid pguidExtendedType);
+
+ ///
+ /// Retrieves an HRESULT that specifies the event status.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetStatus(
+ /// /* [out] */ __RPC__out HRESULT *phrStatus) = 0;
+ ///
+ void GetStatus([MarshalAs(UnmanagedType.Error)] out int phrStatus);
+
+ ///
+ /// Retrieves the value associated with the event, if any.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetValue(
+ /// /* [out] */ __RPC__out PROPVARIANT *pvValue) = 0;
+ ///
+ void GetValue(out PropVariant pvValue);
+ }
+}
diff --git a/NAudio/MediaFoundation/IMFMediaType.cs b/NAudio/MediaFoundation/IMFMediaType.cs
new file mode 100644
index 00000000..0b2dc802
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFMediaType.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Represents a description of a media format.
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms704850%28v=vs.85%29.aspx
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("44AE0FA8-EA31-4109-8D2E-4CAE4997C555")]
+ public interface IMFMediaType : IMFAttributes
+ {
+ ///
+ /// Retrieves the value associated with a key.
+ ///
+ new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Retrieves the data type of the value associated with a key.
+ ///
+ new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pType);
+
+ ///
+ /// Queries whether a stored attribute value equals a specified PROPVARIANT.
+ ///
+ new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Compares the attributes on this object with the attributes on another object.
+ ///
+ new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Retrieves a UINT32 value associated with a key.
+ ///
+ new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);
+
+ ///
+ /// Retrieves a UINT64 value associated with a key.
+ ///
+ new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);
+
+ ///
+ /// Retrieves a double value associated with a key.
+ ///
+ new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);
+
+ ///
+ /// Retrieves a GUID value associated with a key.
+ ///
+ new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);
+
+ ///
+ /// Retrieves the length of a string value associated with a key.
+ ///
+ new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key.
+ ///
+ new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize,
+ out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key. This method allocates the memory for the string.
+ ///
+ new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue,
+ out int pcchLength);
+
+ ///
+ /// Retrieves the length of a byte array associated with a key.
+ ///
+ new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key.
+ ///
+ new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize,
+ out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key. This method allocates the memory for the array.
+ ///
+ new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);
+
+ ///
+ /// Retrieves an interface pointer associated with a key.
+ ///
+ new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
+ [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
+
+ ///
+ /// Associates an attribute value with a key.
+ ///
+ new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);
+
+ ///
+ /// Removes a key/value pair from the object's attribute list.
+ ///
+ new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);
+
+ ///
+ /// Removes all key/value pairs from the object's attribute list.
+ ///
+ new void DeleteAllItems();
+
+ ///
+ /// Associates a UINT32 value with a key.
+ ///
+ new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);
+
+ ///
+ /// Associates a UINT64 value with a key.
+ ///
+ new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);
+
+ ///
+ /// Associates a double value with a key.
+ ///
+ new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);
+
+ ///
+ /// Associates a GUID value with a key.
+ ///
+ new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);
+
+ ///
+ /// Associates a wide-character string with a key.
+ ///
+ new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);
+
+ ///
+ /// Associates a byte array with a key.
+ ///
+ new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf,
+ int cbBufSize);
+
+ ///
+ /// Associates an IUnknown pointer with a key.
+ ///
+ new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
+
+ ///
+ /// Locks the attribute store so that no other thread can access it.
+ ///
+ new void LockStore();
+
+ ///
+ /// Unlocks the attribute store.
+ ///
+ new void UnlockStore();
+
+ ///
+ /// Retrieves the number of attributes that are set on this object.
+ ///
+ new void GetCount(out int pcItems);
+
+ ///
+ /// Retrieves an attribute at the specified index.
+ ///
+ new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Copies all of the attributes from this object into another attribute store.
+ ///
+ new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);
+
+ ///
+ /// Retrieves the major type of the format.
+ ///
+ void GetMajorType(out Guid pguidMajorType);
+
+ ///
+ /// Queries whether the media type is a compressed format.
+ ///
+ void IsCompressedFormat([MarshalAs(UnmanagedType.Bool)] out bool pfCompressed);
+
+ ///
+ /// Compares two media types and determines whether they are identical.
+ ///
+ [PreserveSig]
+ int IsEqual([In, MarshalAs(UnmanagedType.Interface)] IMFMediaType pIMediaType, ref int pdwFlags);
+
+ ///
+ /// Retrieves an alternative representation of the media type.
+ ///
+
+ void GetRepresentation([In, MarshalAs(UnmanagedType.Struct)] Guid guidRepresentation, ref IntPtr ppvRepresentation);
+
+ ///
+ /// Frees memory that was allocated by the GetRepresentation method.
+ ///
+ void FreeRepresentation([In, MarshalAs(UnmanagedType.Struct)] Guid guidRepresentation, [In] IntPtr pvRepresentation);
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/IMFReadWriteClassFactory.cs b/NAudio/MediaFoundation/IMFReadWriteClassFactory.cs
new file mode 100644
index 00000000..f9cb36ec
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFReadWriteClassFactory.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Creates an instance of either the sink writer or the source reader.
+ ///
+ [ComImport,Guid("E7FE2E12-661C-40DA-92F9-4F002AB67627"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IMFReadWriteClassFactory
+ {
+ ///
+ /// Creates an instance of the sink writer or source reader, given a URL.
+ ///
+ void CreateInstanceFromURL([In, MarshalAs(UnmanagedType.LPStruct)] Guid clsid, [In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, [In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pAttributes, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+
+ ///
+ /// Creates an instance of the sink writer or source reader, given an IUnknown pointer.
+ ///
+ void CreateInstanceFromObject([In, MarshalAs(UnmanagedType.LPStruct)] Guid clsid, [In, MarshalAs(UnmanagedType.IUnknown)] object punkObject, [In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pAttributes, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out, MarshalAs(UnmanagedType.Interface)] out object ppvObject);
+ }
+
+ ///
+ /// CLSID_MFReadWriteClassFactory
+ ///
+ [ComImport, Guid("48e2ed0f-98c2-4a37-bed5-166312ddd83f")]
+ public class MFReadWriteClassFactory
+ {
+ }
+}
diff --git a/NAudio/MediaFoundation/IMFSample.cs b/NAudio/MediaFoundation/IMFSample.cs
new file mode 100644
index 00000000..ac5704c5
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFSample.cs
@@ -0,0 +1,239 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// http://msdn.microsoft.com/en-gb/library/windows/desktop/ms702192%28v=vs.85%29.aspx
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("c40a00f2-b93a-4d80-ae8c-5a1c634f58e4")]
+ public interface IMFSample : IMFAttributes
+ {
+ ///
+ /// Retrieves the value associated with a key.
+ ///
+ new void GetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Retrieves the data type of the value associated with a key.
+ ///
+ new void GetItemType([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pType);
+
+ ///
+ /// Queries whether a stored attribute value equals a specified PROPVARIANT.
+ ///
+ new void CompareItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Compares the attributes on this object with the attributes on another object.
+ ///
+ new void Compare([MarshalAs(UnmanagedType.Interface)] IMFAttributes pTheirs, int MatchType, [MarshalAs(UnmanagedType.Bool)] out bool pbResult);
+
+ ///
+ /// Retrieves a UINT32 value associated with a key.
+ ///
+ new void GetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int punValue);
+
+ ///
+ /// Retrieves a UINT64 value associated with a key.
+ ///
+ new void GetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out long punValue);
+
+ ///
+ /// Retrieves a double value associated with a key.
+ ///
+ new void GetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out double pfValue);
+
+ ///
+ /// Retrieves a GUID value associated with a key.
+ ///
+ new void GetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out Guid pguidValue);
+
+ ///
+ /// Retrieves the length of a string value associated with a key.
+ ///
+ new void GetStringLength([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key.
+ ///
+ new void GetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwszValue, int cchBufSize,
+ out int pcchLength);
+
+ ///
+ /// Retrieves a wide-character string associated with a key. This method allocates the memory for the string.
+ ///
+ new void GetAllocatedString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] out string ppwszValue,
+ out int pcchLength);
+
+ ///
+ /// Retrieves the length of a byte array associated with a key.
+ ///
+ new void GetBlobSize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key.
+ ///
+ new void GetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pBuf, int cbBufSize,
+ out int pcbBlobSize);
+
+ ///
+ /// Retrieves a byte array associated with a key. This method allocates the memory for the array.
+ ///
+ new void GetAllocatedBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, out IntPtr ip, out int pcbSize);
+
+ ///
+ /// Retrieves an interface pointer associated with a key.
+ ///
+ new void GetUnknown([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
+ [MarshalAs(UnmanagedType.IUnknown)] out object ppv);
+
+ ///
+ /// Associates an attribute value with a key.
+ ///
+ new void SetItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, IntPtr Value);
+
+ ///
+ /// Removes a key/value pair from the object's attribute list.
+ ///
+ new void DeleteItem([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey);
+
+ ///
+ /// Removes all key/value pairs from the object's attribute list.
+ ///
+ new void DeleteAllItems();
+
+ ///
+ /// Associates a UINT32 value with a key.
+ ///
+ new void SetUINT32([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, int unValue);
+
+ ///
+ /// Associates a UINT64 value with a key.
+ ///
+ new void SetUINT64([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, long unValue);
+
+ ///
+ /// Associates a double value with a key.
+ ///
+ new void SetDouble([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, double fValue);
+
+ ///
+ /// Associates a GUID value with a key.
+ ///
+ new void SetGUID([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidValue);
+
+ ///
+ /// Associates a wide-character string with a key.
+ ///
+ new void SetString([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPWStr)] string wszValue);
+
+ ///
+ /// Associates a byte array with a key.
+ ///
+ new void SetBlob([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBuf,
+ int cbBufSize);
+
+ ///
+ /// Associates an IUnknown pointer with a key.
+ ///
+ new void SetUnknown([MarshalAs(UnmanagedType.LPStruct)] Guid guidKey, [In, MarshalAs(UnmanagedType.IUnknown)] object pUnknown);
+
+ ///
+ /// Locks the attribute store so that no other thread can access it.
+ ///
+ new void LockStore();
+
+ ///
+ /// Unlocks the attribute store.
+ ///
+ new void UnlockStore();
+
+ ///
+ /// Retrieves the number of attributes that are set on this object.
+ ///
+ new void GetCount(out int pcItems);
+
+ ///
+ /// Retrieves an attribute at the specified index.
+ ///
+ new void GetItemByIndex(int unIndex, out Guid pGuidKey, [In, Out] ref PropVariant pValue);
+
+ ///
+ /// Copies all of the attributes from this object into another attribute store.
+ ///
+ new void CopyAllItems([In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pDest);
+
+ ///
+ /// Retrieves flags associated with the sample.
+ ///
+ void GetSampleFlags(out int pdwSampleFlags);
+
+ ///
+ /// Sets flags associated with the sample.
+ ///
+ void SetSampleFlags(int dwSampleFlags);
+
+ ///
+ /// Retrieves the presentation time of the sample.
+ ///
+ void GetSampleTime(out long phnsSampletime);
+
+ ///
+ /// Sets the presentation time of the sample.
+ ///
+ void SetSampleTime(long hnsSampleTime);
+
+ ///
+ /// Retrieves the duration of the sample.
+ ///
+ void GetSampleDuration(out long phnsSampleDuration);
+
+ ///
+ /// Sets the duration of the sample.
+ ///
+ void SetSampleDuration(long hnsSampleDuration);
+
+ ///
+ /// Retrieves the number of buffers in the sample.
+ ///
+ void GetBufferCount(out int pdwBufferCount);
+
+ ///
+ /// Retrieves a buffer from the sample.
+ ///
+ void GetBufferByIndex(int dwIndex, out IMFMediaBuffer ppBuffer);
+
+ ///
+ /// Converts a sample with multiple buffers into a sample with a single buffer.
+ ///
+ void ConvertToContiguousBuffer(out IMFMediaBuffer ppBuffer);
+
+ ///
+ /// Adds a buffer to the end of the list of buffers in the sample.
+ ///
+ void AddBuffer(IMFMediaBuffer pBuffer);
+
+ ///
+ /// Removes a buffer at a specified index from the sample.
+ ///
+ void RemoveBufferByIndex(int dwIndex);
+
+ ///
+ /// Removes all buffers from the sample.
+ ///
+ void RemoveAllBuffers();
+
+ ///
+ /// Retrieves the total length of the valid data in all of the buffers in the sample.
+ ///
+ void GetTotalLength(out int pcbTotalLength);
+
+ ///
+ /// Copies the sample data to a buffer.
+ ///
+ void CopyToBuffer(IMFMediaBuffer pBuffer);
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/IMFSinkWriter.cs b/NAudio/MediaFoundation/IMFSinkWriter.cs
new file mode 100644
index 00000000..44e584fc
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFSinkWriter.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Implemented by the Microsoft Media Foundation sink writer object.
+ ///
+ [ComImport, Guid("3137f1cd-fe5e-4805-a5d8-fb477448cb3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+ public interface IMFSinkWriter
+ {
+ ///
+ /// Adds a stream to the sink writer.
+ ///
+ void AddStream([In, MarshalAs(UnmanagedType.Interface)] IMFMediaType pTargetMediaType, out int pdwStreamIndex);
+ ///
+ /// Sets the input format for a stream on the sink writer.
+ ///
+ void SetInputMediaType([In] int dwStreamIndex, [In, MarshalAs(UnmanagedType.Interface)] IMFMediaType pInputMediaType, [In, MarshalAs(UnmanagedType.Interface)] IMFAttributes pEncodingParameters);
+ ///
+ /// Initializes the sink writer for writing.
+ ///
+ void BeginWriting();
+ ///
+ /// Delivers a sample to the sink writer.
+ ///
+ void WriteSample([In] int dwStreamIndex, [In, MarshalAs(UnmanagedType.Interface)] IMFSample pSample);
+ ///
+ /// Indicates a gap in an input stream.
+ ///
+ void SendStreamTick([In] int dwStreamIndex, [In] long llTimestamp);
+ ///
+ /// Places a marker in the specified stream.
+ ///
+ void PlaceMarker([In] int dwStreamIndex, [In] IntPtr pvContext);
+ ///
+ /// Notifies the media sink that a stream has reached the end of a segment.
+ ///
+ void NotifyEndOfSegment([In] int dwStreamIndex);
+ ///
+ /// Flushes one or more streams.
+ ///
+ void Flush([In] int dwStreamIndex);
+ ///
+ /// (Finalize) Completes all writing operations on the sink writer.
+ ///
+ void DoFinalize();
+ ///
+ /// Queries the underlying media sink or encoder for an interface.
+ ///
+ void GetServiceForStream([In] int dwStreamIndex, [In] ref Guid guidService, [In] ref Guid riid, out IntPtr ppvObject);
+ ///
+ /// Gets statistics about the performance of the sink writer.
+ ///
+ void GetStatistics([In] int dwStreamIndex, [In, Out] MF_SINK_WRITER_STATISTICS pStats);
+ }
+}
diff --git a/NAudio/MediaFoundation/IMFSourceReader.cs b/NAudio/MediaFoundation/IMFSourceReader.cs
new file mode 100644
index 00000000..089f7b8b
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFSourceReader.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// IMFSourceReader interface
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd374655%28v=vs.85%29.aspx
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("70ae66f2-c809-4e4f-8915-bdcb406b7993")]
+ public interface IMFSourceReader
+ {
+ ///
+ /// Queries whether a stream is selected.
+ ///
+ void GetStreamSelection([In] int dwStreamIndex, [Out, MarshalAs(UnmanagedType.Bool)] out bool pSelected);
+ ///
+ /// Selects or deselects one or more streams.
+ ///
+ void SetStreamSelection([In] int dwStreamIndex, [In, MarshalAs(UnmanagedType.Bool)] bool pSelected);
+ ///
+ /// Gets a format that is supported natively by the media source.
+ ///
+ void GetNativeMediaType([In] int dwStreamIndex, [In] int dwMediaTypeIndex, [Out] out IMFMediaType ppMediaType);
+ ///
+ /// Gets the current media type for a stream.
+ ///
+ void GetCurrentMediaType([In] int dwStreamIndex, [Out] out IMFMediaType ppMediaType);
+ ///
+ /// Sets the media type for a stream.
+ ///
+ void SetCurrentMediaType([In] int dwStreamIndex, IntPtr pdwReserved, [In] IMFMediaType pMediaType);
+ ///
+ /// Seeks to a new position in the media source.
+ ///
+ void SetCurrentPosition([In, MarshalAs(UnmanagedType.LPStruct)] Guid guidTimeFormat, [In] ref PropVariant varPosition);
+ ///
+ /// Reads the next sample from the media source.
+ ///
+ void ReadSample([In] int dwStreamIndex, [In] int dwControlFlags, [Out] out int pdwActualStreamIndex, [Out] out MF_SOURCE_READER_FLAG pdwStreamFlags,
+ [Out] out UInt64 pllTimestamp, [Out] out IMFSample ppSample);
+ ///
+ /// Flushes one or more streams.
+ ///
+ void Flush([In] int dwStreamIndex);
+
+ ///
+ /// Queries the underlying media source or decoder for an interface.
+ ///
+ void GetServiceForStream([In] int dwStreamIndex, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidService,
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [Out] out IntPtr ppvObject);
+
+ ///
+ /// Gets an attribute from the underlying media source.
+ ///
+ [PreserveSig]
+ int GetPresentationAttribute([In] int dwStreamIndex, [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidAttribute, [Out] out PropVariant pvarAttribute);
+ }
+
+ ///
+ /// Contains flags that indicate the status of the IMFSourceReader::ReadSample method
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/dd375773(v=vs.85).aspx
+ ///
+ [Flags]
+ public enum MF_SOURCE_READER_FLAG {
+ ///
+ /// No Error
+ ///
+ None = 0,
+ ///
+ /// An error occurred. If you receive this flag, do not make any further calls to IMFSourceReader methods.
+ ///
+ MF_SOURCE_READERF_ERROR = 0x00000001,
+ ///
+ /// The source reader reached the end of the stream.
+ ///
+ MF_SOURCE_READERF_ENDOFSTREAM = 0x00000002,
+ ///
+ /// One or more new streams were created
+ ///
+ MF_SOURCE_READERF_NEWSTREAM = 0x00000004,
+ ///
+ /// The native format has changed for one or more streams. The native format is the format delivered by the media source before any decoders are inserted.
+ ///
+ MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED = 0x00000010,
+ ///
+ /// The current media has type changed for one or more streams. To get the current media type, call the IMFSourceReader::GetCurrentMediaType method.
+ ///
+ MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED = 0x00000020,
+ ///
+ /// There is a gap in the stream. This flag corresponds to an MEStreamTick event from the media source.
+ ///
+ MF_SOURCE_READERF_STREAMTICK = 0x00000100,
+ ///
+ /// All transforms inserted by the application have been removed for a particular stream.
+ ///
+ MF_SOURCE_READERF_ALLEFFECTSREMOVED = 0x00000200
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/IMFTransform.cs b/NAudio/MediaFoundation/IMFTransform.cs
new file mode 100644
index 00000000..7d56b8e0
--- /dev/null
+++ b/NAudio/MediaFoundation/IMFTransform.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// IMFTransform, defined in mftransform.h
+ ///
+ [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("bf94c121-5b05-4e6f-8000-ba598961414d")]
+ public interface IMFTransform
+ {
+ ///
+ /// Retrieves the minimum and maximum number of input and output streams.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetStreamLimits(
+ /// /* [out] */ __RPC__out DWORD *pdwInputMinimum,
+ /// /* [out] */ __RPC__out DWORD *pdwInputMaximum,
+ /// /* [out] */ __RPC__out DWORD *pdwOutputMinimum,
+ /// /* [out] */ __RPC__out DWORD *pdwOutputMaximum) = 0;
+ ///
+ void GetStreamLimits([Out] out int pdwInputMinimum, [Out] out int pdwInputMaximum, [Out] out int pdwOutputMinimum, [Out] out int pdwOutputMaximum);
+
+ ///
+ /// Retrieves the current number of input and output streams on this MFT.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetStreamCount(
+ /// /* [out] */ __RPC__out DWORD *pcInputStreams,
+ /// /* [out] */ __RPC__out DWORD *pcOutputStreams) = 0;
+ ///
+ void GetStreamCount([Out] out int pcInputStreams, [Out] out int pcOutputStreams);
+
+ ///
+ /// Retrieves the stream identifiers for the input and output streams on this MFT.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetStreamIDs(
+ /// DWORD dwInputIDArraySize,
+ /// /* [size_is][out] */ __RPC__out_ecount_full(dwInputIDArraySize) DWORD *pdwInputIDs,
+ /// DWORD dwOutputIDArraySize,
+ /// /* [size_is][out] */ __RPC__out_ecount_full(dwOutputIDArraySize) DWORD *pdwOutputIDs) = 0;
+ ///
+ void GetStreamIds([In] int dwInputIDArraySize, [In, Out] int[] pdwInputIDs, [In] int dwOutputIDArraySize, [In, Out] int[] pdwOutputIDs);
+
+ ///
+ /// Gets the buffer requirements and other information for an input stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetInputStreamInfo(
+ /// DWORD dwInputStreamID,
+ /// /* [out] */ __RPC__out MFT_INPUT_STREAM_INFO *pStreamInfo) = 0;
+ ///
+ void GetInputStreamInfo([In] int dwInputStreamID, [Out] out MFT_INPUT_STREAM_INFO pStreamInfo);
+
+ ///
+ /// Gets the buffer requirements and other information for an output stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetOutputStreamInfo(
+ /// DWORD dwOutputStreamID,
+ /// /* [out] */ __RPC__out MFT_OUTPUT_STREAM_INFO *pStreamInfo) = 0;
+ ///
+ void GetOutputStreamInfo([In] int dwOutputStreamID, [Out] out MFT_OUTPUT_STREAM_INFO pStreamInfo);
+
+ ///
+ /// Gets the global attribute store for this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetAttributes(
+ /// /* [out] */ __RPC__deref_out_opt IMFAttributes **pAttributes) = 0;
+ ///
+ void GetAttributes([Out] out IMFAttributes pAttributes);
+
+ ///
+ /// Retrieves the attribute store for an input stream on this MFT.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetInputStreamAttributes(
+ /// DWORD dwInputStreamID,
+ /// /* [out] */ __RPC__deref_out_opt IMFAttributes **pAttributes) = 0;
+ ///
+ void GetInputStreamAttributes([In] int dwInputStreamID, [Out] out IMFAttributes pAttributes);
+
+ ///
+ /// Retrieves the attribute store for an output stream on this MFT.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetOutputStreamAttributes(
+ /// DWORD dwOutputStreamID,
+ /// /* [out] */ __RPC__deref_out_opt IMFAttributes **pAttributes) = 0;
+ ///
+ void GetOutputStreamAttributes([In] int dwOutputStreamID, [Out] out IMFAttributes pAttributes);
+
+ ///
+ /// Removes an input stream from this MFT.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE DeleteInputStream(
+ /// DWORD dwStreamID) = 0;
+ ///
+ void DeleteInputStream([In] int dwOutputStreamID);
+
+ ///
+ /// Adds one or more new input streams to this MFT.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE AddInputStreams(
+ /// DWORD cStreams,
+ /// /* [in] */ __RPC__in DWORD *adwStreamIDs) = 0;
+ ///
+ void AddInputStreams([In] int cStreams, [In] int[] adwStreamIDs);
+
+ ///
+ /// Gets an available media type for an input stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetInputAvailableType(
+ /// DWORD dwInputStreamID,
+ /// DWORD dwTypeIndex,
+ /// /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType) = 0;
+ ///
+ void GetInputAvailableType([In] int dwInputStreamID, [In] int dwTypeIndex, [Out] out IMFMediaType ppType);
+
+ ///
+ /// Retrieves an available media type for an output stream on this MFT.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetOutputAvailableType(
+ /// DWORD dwOutputStreamID,
+ /// DWORD dwTypeIndex,
+ /// /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType) = 0;
+ ///
+ void GetOutputAvailableType([In] int dwOutputStreamID, [In] int dwTypeIndex, [Out] out IMFMediaType ppType);
+
+ ///
+ /// Sets, tests, or clears the media type for an input stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE SetInputType(
+ /// DWORD dwInputStreamID,
+ /// /* [in] */ __RPC__in_opt IMFMediaType *pType,
+ /// DWORD dwFlags) = 0;
+ ///
+ void SetInputType([In] int dwInputStreamID, [In] IMFMediaType pType, [In] _MFT_SET_TYPE_FLAGS dwFlags);
+
+ ///
+ /// Sets, tests, or clears the media type for an output stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE SetOutputType(
+ /// DWORD dwOutputStreamID,
+ /// /* [in] */ __RPC__in_opt IMFMediaType *pType,
+ /// DWORD dwFlags) = 0;
+ ///
+ void SetOutputType([In] int dwOutputStreamID, [In] IMFMediaType pType, [In] _MFT_SET_TYPE_FLAGS dwFlags);
+
+ ///
+ /// Gets the current media type for an input stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetInputCurrentType(
+ /// DWORD dwInputStreamID,
+ /// /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType) = 0;
+ ///
+ void GetInputCurrentType([In] int dwInputStreamID, [Out] out IMFMediaType ppType);
+
+ ///
+ /// Gets the current media type for an output stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetOutputCurrentType(
+ /// DWORD dwOutputStreamID,
+ /// /* [out] */ __RPC__deref_out_opt IMFMediaType **ppType) = 0;
+ ///
+ void GetOutputCurrentType([In] int dwOutputStreamID, [Out] out IMFMediaType ppType);
+
+ ///
+ /// Queries whether an input stream on this Media Foundation transform (MFT) can accept more data.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetInputStatus(
+ /// DWORD dwInputStreamID,
+ /// /* [out] */ __RPC__out DWORD *pdwFlags) = 0;
+ ///
+ void GetInputStatus([In] int dwInputStreamID, [Out] out _MFT_INPUT_STATUS_FLAGS pdwFlags);
+
+ ///
+ /// Queries whether the Media Foundation transform (MFT) is ready to produce output data.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE GetOutputStatus(
+ /// /* [out] */ __RPC__out DWORD *pdwFlags) = 0;
+ ///
+ void GetOutputStatus([In] int dwInputStreamID, [Out] out _MFT_OUTPUT_STATUS_FLAGS pdwFlags);
+
+ ///
+ /// Sets the range of time stamps the client needs for output.
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE SetOutputBounds(
+ /// LONGLONG hnsLowerBound,
+ /// LONGLONG hnsUpperBound) = 0;
+ ///
+ void SetOutputBounds([In] long hnsLowerBound, [In] long hnsUpperBound);
+
+ ///
+ /// Sends an event to an input stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE ProcessEvent(
+ /// DWORD dwInputStreamID,
+ /// /* [in] */ __RPC__in_opt IMFMediaEvent *pEvent) = 0;
+ ///
+ void ProcessEvent([In] int dwInputStreamID, [In] IMFMediaEvent pEvent);
+
+ ///
+ /// Sends a message to the Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual HRESULT STDMETHODCALLTYPE ProcessMessage(
+ /// MFT_MESSAGE_TYPE eMessage,
+ /// ULONG_PTR ulParam) = 0;
+ ///
+ void ProcessMessage([In] MFT_MESSAGE_TYPE eMessage, [In] IntPtr ulParam);
+
+ ///
+ /// Delivers data to an input stream on this Media Foundation transform (MFT).
+ ///
+ ///
+ /// virtual /* [local] */ HRESULT STDMETHODCALLTYPE ProcessInput(
+ /// DWORD dwInputStreamID,
+ /// IMFSample *pSample,
+ /// DWORD dwFlags) = 0;
+ ///
+ void ProcessInput([In] int dwInputStreamID, [In] IMFSample pSample, int dwFlags);
+
+ ///
+ /// Generates output from the current input data.
+ ///
+ ///
+ /// virtual /* [local] */ HRESULT STDMETHODCALLTYPE ProcessOutput(
+ /// DWORD dwFlags,
+ /// DWORD cOutputBufferCount,
+ /// /* [size_is][out][in] */ MFT_OUTPUT_DATA_BUFFER *pOutputSamples,
+ /// /* [out] */ DWORD *pdwStatus) = 0;
+ ///
+ [PreserveSig]
+ int ProcessOutput([In] _MFT_PROCESS_OUTPUT_FLAGS dwFlags,
+ [In] int cOutputBufferCount,
+ [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] MFT_OUTPUT_DATA_BUFFER[] pOutputSamples,
+ [Out] out _MFT_PROCESS_OUTPUT_STATUS pdwStatus);
+ }
+}
diff --git a/NAudio/MediaFoundation/MFT_INPUT_STREAM_INFO.cs b/NAudio/MediaFoundation/MFT_INPUT_STREAM_INFO.cs
new file mode 100644
index 00000000..358c8ce7
--- /dev/null
+++ b/NAudio/MediaFoundation/MFT_INPUT_STREAM_INFO.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Contains information about an input stream on a Media Foundation transform (MFT)
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MFT_INPUT_STREAM_INFO
+ {
+ ///
+ /// Maximum amount of time between an input sample and the corresponding output sample, in 100-nanosecond units.
+ ///
+ public long hnsMaxLatency;
+ ///
+ /// Bitwise OR of zero or more flags from the _MFT_INPUT_STREAM_INFO_FLAGS enumeration.
+ ///
+ public _MFT_INPUT_STREAM_INFO_FLAGS dwFlags;
+ ///
+ /// The minimum size of each input buffer, in bytes.
+ ///
+ public int cbSize;
+ ///
+ /// Maximum amount of input data, in bytes, that the MFT holds to perform lookahead.
+ ///
+ public int cbMaxLookahead;
+ ///
+ /// The memory alignment required for input buffers. If the MFT does not require a specific alignment, the value is zero.
+ ///
+ public int cbAlignment;
+ }
+}
diff --git a/NAudio/MediaFoundation/MFT_MESSAGE_TYPE.cs b/NAudio/MediaFoundation/MFT_MESSAGE_TYPE.cs
new file mode 100644
index 00000000..40998b1d
--- /dev/null
+++ b/NAudio/MediaFoundation/MFT_MESSAGE_TYPE.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Defines messages for a Media Foundation transform (MFT).
+ ///
+ public enum MFT_MESSAGE_TYPE
+ {
+ ///
+ /// Requests the MFT to flush all stored data.
+ ///
+ MFT_MESSAGE_COMMAND_FLUSH = 0x00000000,
+ ///
+ /// Requests the MFT to drain any stored data.
+ ///
+ MFT_MESSAGE_COMMAND_DRAIN = 0x00000001,
+ ///
+ /// Sets or clears the Direct3D Device Manager for DirectX Video Accereration (DXVA).
+ ///
+ MFT_MESSAGE_SET_D3D_MANAGER = 0x00000002,
+ ///
+ /// Drop samples - requires Windows 7
+ ///
+ MFT_MESSAGE_DROP_SAMPLES = 0x00000003,
+ ///
+ /// Command Tick - requires Windows 8
+ ///
+ MFT_MESSAGE_COMMAND_TICK = 0x00000004,
+ ///
+ /// Notifies the MFT that streaming is about to begin.
+ ///
+ MFT_MESSAGE_NOTIFY_BEGIN_STREAMING = 0x10000000,
+ ///
+ /// Notifies the MFT that streaming is about to end.
+ ///
+ MFT_MESSAGE_NOTIFY_END_STREAMING = 0x10000001,
+ ///
+ /// Notifies the MFT that an input stream has ended.
+ ///
+ MFT_MESSAGE_NOTIFY_END_OF_STREAM = 0x10000002,
+ ///
+ /// Notifies the MFT that the first sample is about to be processed.
+ ///
+ MFT_MESSAGE_NOTIFY_START_OF_STREAM = 0x10000003,
+ ///
+ /// Marks a point in the stream. This message applies only to asynchronous MFTs. Requires Windows 7
+ ///
+ MFT_MESSAGE_COMMAND_MARKER = 0x20000000
+ }
+}
diff --git a/NAudio/MediaFoundation/MFT_OUTPUT_DATA_BUFFER.cs b/NAudio/MediaFoundation/MFT_OUTPUT_DATA_BUFFER.cs
new file mode 100644
index 00000000..a66136ad
--- /dev/null
+++ b/NAudio/MediaFoundation/MFT_OUTPUT_DATA_BUFFER.cs
@@ -0,0 +1,28 @@
+using System.Runtime.InteropServices;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Contains information about an output buffer for a Media Foundation transform.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MFT_OUTPUT_DATA_BUFFER
+ {
+ ///
+ /// Output stream identifier.
+ ///
+ public int dwStreamID;
+ ///
+ /// Pointer to the IMFSample interface.
+ ///
+ public IMFSample pSample;
+ ///
+ /// Before calling ProcessOutput, set this member to zero.
+ ///
+ public _MFT_OUTPUT_DATA_BUFFER_FLAGS dwStatus;
+ ///
+ /// Before calling ProcessOutput, set this member to NULL.
+ ///
+ public IMFCollection pEvents;
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/MFT_OUTPUT_STREAM_INFO.cs b/NAudio/MediaFoundation/MFT_OUTPUT_STREAM_INFO.cs
new file mode 100644
index 00000000..085ca046
--- /dev/null
+++ b/NAudio/MediaFoundation/MFT_OUTPUT_STREAM_INFO.cs
@@ -0,0 +1,24 @@
+using System.Runtime.InteropServices;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Contains information about an output stream on a Media Foundation transform (MFT).
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MFT_OUTPUT_STREAM_INFO
+ {
+ ///
+ /// Bitwise OR of zero or more flags from the _MFT_OUTPUT_STREAM_INFO_FLAGS enumeration.
+ ///
+ public _MFT_OUTPUT_STREAM_INFO_FLAGS dwFlags;
+ ///
+ /// Minimum size of each output buffer, in bytes.
+ ///
+ public int cbSize;
+ ///
+ /// The memory alignment required for output buffers.
+ ///
+ public int cbAlignment;
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/MFT_REGISTER_TYPE_INFO.cs b/NAudio/MediaFoundation/MFT_REGISTER_TYPE_INFO.cs
new file mode 100644
index 00000000..6faf2960
--- /dev/null
+++ b/NAudio/MediaFoundation/MFT_REGISTER_TYPE_INFO.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Contains media type information for registering a Media Foundation transform (MFT).
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class MFT_REGISTER_TYPE_INFO
+ {
+ ///
+ /// The major media type.
+ ///
+ public Guid guidMajorType;
+ ///
+ /// The Media Subtype
+ ///
+ public Guid guidSubtype;
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/MF_SINK_WRITER_STATISTICS.cs b/NAudio/MediaFoundation/MF_SINK_WRITER_STATISTICS.cs
new file mode 100644
index 00000000..10662b6c
--- /dev/null
+++ b/NAudio/MediaFoundation/MF_SINK_WRITER_STATISTICS.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Contains statistics about the performance of the sink writer.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class MF_SINK_WRITER_STATISTICS
+ {
+ ///
+ /// The size of the structure, in bytes.
+ ///
+ public int cb;
+ ///
+ /// The time stamp of the most recent sample given to the sink writer.
+ ///
+ public long llLastTimestampReceived;
+ ///
+ /// The time stamp of the most recent sample to be encoded.
+ ///
+ public long llLastTimestampEncoded;
+ ///
+ /// The time stamp of the most recent sample given to the media sink.
+ ///
+ public long llLastTimestampProcessed;
+ ///
+ /// The time stamp of the most recent stream tick.
+ ///
+ public long llLastStreamTickReceived;
+ ///
+ /// The system time of the most recent sample request from the media sink.
+ ///
+ public long llLastSinkSampleRequest;
+ ///
+ /// The number of samples received.
+ ///
+ public long qwNumSamplesReceived;
+ ///
+ /// The number of samples encoded.
+ ///
+ public long qwNumSamplesEncoded;
+ ///
+ /// The number of samples given to the media sink.
+ ///
+ public long qwNumSamplesProcessed;
+ ///
+ /// The number of stream ticks received.
+ ///
+ public long qwNumStreamTicksReceived;
+ ///
+ /// The amount of data, in bytes, currently waiting to be processed.
+ ///
+ public int dwByteCountQueued;
+ ///
+ /// The total amount of data, in bytes, that has been sent to the media sink.
+ ///
+ public long qwByteCountProcessed;
+ ///
+ /// The number of pending sample requests.
+ ///
+ public int dwNumOutstandingSinkSampleRequests;
+ ///
+ /// The average rate, in media samples per 100-nanoseconds, at which the application sent samples to the sink writer.
+ ///
+ public int dwAverageSampleRateReceived;
+ ///
+ /// The average rate, in media samples per 100-nanoseconds, at which the sink writer sent samples to the encoder
+ ///
+ public int dwAverageSampleRateEncoded;
+ ///
+ /// The average rate, in media samples per 100-nanoseconds, at which the sink writer sent samples to the media sink.
+ ///
+ public int dwAverageSampleRateProcessed;
+ }
+}
diff --git a/NAudio/MediaFoundation/MediaEventType.cs b/NAudio/MediaFoundation/MediaEventType.cs
new file mode 100644
index 00000000..0d586776
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaEventType.cs
@@ -0,0 +1,437 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// See mfobjects.h
+ ///
+ public enum MediaEventType
+ {
+ ///
+ /// Unknown event type.
+ ///
+ MEUnknown = 0,
+ ///
+ /// Signals a serious error.
+ ///
+ MEError = 1,
+ ///
+ /// Custom event type.
+ ///
+ MEExtendedType = 2,
+ ///
+ /// A non-fatal error occurred during streaming.
+ ///
+ MENonFatalError = 3,
+ // MEGenericV1Anchor = MENonFatalError,
+ ///
+ /// Session Unknown
+ ///
+ MESessionUnknown = 100,
+ ///
+ /// Raised after the IMFMediaSession::SetTopology method completes asynchronously
+ ///
+ MESessionTopologySet = 101,
+ ///
+ /// Raised by the Media Session when the IMFMediaSession::ClearTopologies method completes asynchronously.
+ ///
+ MESessionTopologiesCleared = 102,
+ ///
+ /// Raised when the IMFMediaSession::Start method completes asynchronously.
+ ///
+ MESessionStarted = 103,
+ ///
+ /// Raised when the IMFMediaSession::Pause method completes asynchronously.
+ ///
+ MESessionPaused = 104,
+ ///
+ /// Raised when the IMFMediaSession::Stop method completes asynchronously.
+ ///
+ MESessionStopped = 105,
+ ///
+ /// Raised when the IMFMediaSession::Close method completes asynchronously.
+ ///
+ MESessionClosed = 106,
+ ///
+ /// Raised by the Media Session when it has finished playing the last presentation in the playback queue.
+ ///
+ MESessionEnded = 107,
+ ///
+ /// Raised by the Media Session when the playback rate changes.
+ ///
+ MESessionRateChanged = 108,
+ ///
+ /// Raised by the Media Session when it completes a scrubbing request.
+ ///
+ MESessionScrubSampleComplete = 109,
+ ///
+ /// Raised by the Media Session when the session capabilities change.
+ ///
+ MESessionCapabilitiesChanged = 110,
+ ///
+ /// Raised by the Media Session when the status of a topology changes.
+ ///
+ MESessionTopologyStatus = 111,
+ ///
+ /// Raised by the Media Session when a new presentation starts.
+ ///
+ MESessionNotifyPresentationTime = 112,
+ ///
+ /// Raised by a media source a new presentation is ready.
+ ///
+ MENewPresentation = 113,
+ ///
+ /// License acquisition is about to begin.
+ ///
+ MELicenseAcquisitionStart = 114,
+ ///
+ /// License acquisition is complete.
+ ///
+ MELicenseAcquisitionCompleted = 115,
+ ///
+ /// Individualization is about to begin.
+ ///
+ MEIndividualizationStart = 116,
+ ///
+ /// Individualization is complete.
+ ///
+ MEIndividualizationCompleted = 117,
+ ///
+ /// Signals the progress of a content enabler object.
+ ///
+ MEEnablerProgress = 118,
+ ///
+ /// A content enabler object's action is complete.
+ ///
+ MEEnablerCompleted = 119,
+ ///
+ /// Raised by a trusted output if an error occurs while enforcing the output policy.
+ ///
+ MEPolicyError = 120,
+ ///
+ /// Contains status information about the enforcement of an output policy.
+ ///
+ MEPolicyReport = 121,
+ ///
+ /// A media source started to buffer data.
+ ///
+ MEBufferingStarted = 122,
+
+ ///
+ /// A media source stopped buffering data.
+ ///
+ MEBufferingStopped = 123,
+
+ ///
+ /// The network source started opening a URL.
+ ///
+ MEConnectStart = 124,
+ ///
+ /// The network source finished opening a URL.
+ ///
+ MEConnectEnd = 125,
+ ///
+ /// Raised by a media source at the start of a reconnection attempt.
+ ///
+ MEReconnectStart = 126,
+ ///
+ /// Raised by a media source at the end of a reconnection attempt.
+ ///
+ MEReconnectEnd = 127,
+ ///
+ /// Raised by the enhanced video renderer (EVR) when it receives a user event from the presenter.
+ ///
+ MERendererEvent = 128,
+ ///
+ /// Raised by the Media Session when the format changes on a media sink.
+ ///
+ MESessionStreamSinkFormatChanged = 129,
+ //MESessionV1Anchor = MESessionStreamSinkFormatChanged,
+ ///
+ /// Source Unknown
+ ///
+ MESourceUnknown = 200,
+ ///
+ /// Raised when a media source starts without seeking.
+ ///
+ MESourceStarted = 201,
+ ///
+ /// Raised by a media stream when the source starts without seeking.
+ ///
+ MEStreamStarted = 202,
+ ///
+ /// Raised when a media source seeks to a new position.
+ ///
+ MESourceSeeked = 203,
+ ///
+ /// Raised by a media stream after a call to IMFMediaSource::Start causes a seek in the stream.
+ ///
+ MEStreamSeeked = 204,
+ ///
+ /// Raised by a media source when it starts a new stream.
+ ///
+ MENewStream = 205,
+ ///
+ /// Raised by a media source when it restarts or seeks a stream that is already active.
+ ///
+ MEUpdatedStream = 206,
+ ///
+ /// Raised by a media source when the IMFMediaSource::Stop method completes asynchronously.
+ ///
+ MESourceStopped = 207,
+ ///
+ /// Raised by a media stream when the IMFMediaSource::Stop method completes asynchronously.
+ ///
+ MEStreamStopped = 208,
+ ///
+ /// Raised by a media source when the IMFMediaSource::Pause method completes asynchronously.
+ ///
+ MESourcePaused = 209,
+ ///
+ /// Raised by a media stream when the IMFMediaSource::Pause method completes asynchronously.
+ ///
+ MEStreamPaused = 210,
+ ///
+ /// Raised by a media source when a presentation ends.
+ ///
+ MEEndOfPresentation = 211,
+ ///
+ /// Raised by a media stream when the stream ends.
+ ///
+ MEEndOfStream = 212,
+ ///
+ /// Raised when a media stream delivers a new sample.
+ ///
+ MEMediaSample = 213,
+ ///
+ /// Signals that a media stream does not have data available at a specified time.
+ ///
+ MEStreamTick = 214,
+ ///
+ /// Raised by a media stream when it starts or stops thinning the stream.
+ ///
+ MEStreamThinMode = 215,
+ ///
+ /// Raised by a media stream when the media type of the stream changes.
+ ///
+ MEStreamFormatChanged = 216,
+ ///
+ /// Raised by a media source when the playback rate changes.
+ ///
+ MESourceRateChanged = 217,
+ ///
+ /// Raised by the sequencer source when a segment is completed and is followed by another segment.
+ ///
+ MEEndOfPresentationSegment = 218,
+ ///
+ /// Raised by a media source when the source's characteristics change.
+ ///
+ MESourceCharacteristicsChanged = 219,
+ ///
+ /// Raised by a media source to request a new playback rate.
+ ///
+ MESourceRateChangeRequested = 220,
+ ///
+ /// Raised by a media source when it updates its metadata.
+ ///
+ MESourceMetadataChanged = 221,
+ ///
+ /// Raised by the sequencer source when the IMFSequencerSource::UpdateTopology method completes asynchronously.
+ ///
+ MESequencerSourceTopologyUpdated = 222,
+ //MESourceV1Anchor = MESequencerSourceTopologyUpdated,
+ ///
+ /// Sink Unknown
+ ///
+ MESinkUnknown = 300,
+ ///
+ /// Raised by a stream sink when it completes the transition to the running state.
+ ///
+ MEStreamSinkStarted = 301,
+ ///
+ /// Raised by a stream sink when it completes the transition to the stopped state.
+ ///
+ MEStreamSinkStopped = 302,
+ ///
+ /// Raised by a stream sink when it completes the transition to the paused state.
+ ///
+ MEStreamSinkPaused = 303,
+ ///
+ /// Raised by a stream sink when the rate has changed.
+ ///
+ MEStreamSinkRateChanged = 304,
+ ///
+ /// Raised by a stream sink to request a new media sample from the pipeline.
+ ///
+ MEStreamSinkRequestSample = 305,
+ ///
+ /// Raised by a stream sink after the IMFStreamSink::PlaceMarker method is called.
+ ///
+ MEStreamSinkMarker = 306,
+ ///
+ /// Raised by a stream sink when the stream has received enough preroll data to begin rendering.
+ ///
+ MEStreamSinkPrerolled = 307,
+ ///
+ /// Raised by a stream sink when it completes a scrubbing request.
+ ///
+ MEStreamSinkScrubSampleComplete = 308,
+ ///
+ /// Raised by a stream sink when the sink's media type is no longer valid.
+ ///
+ MEStreamSinkFormatChanged = 309,
+ ///
+ /// Raised by the stream sinks of the EVR if the video device changes.
+ ///
+ MEStreamSinkDeviceChanged = 310,
+ ///
+ /// Provides feedback about playback quality to the quality manager.
+ ///
+ MEQualityNotify = 311,
+ ///
+ /// Raised when a media sink becomes invalid.
+ ///
+ MESinkInvalidated = 312,
+
+ ///
+ /// The audio session display name changed.
+ ///
+ MEAudioSessionNameChanged = 313,
+
+ ///
+ /// The volume or mute state of the audio session changed
+ ///
+ MEAudioSessionVolumeChanged = 314,
+
+ ///
+ /// The audio device was removed.
+ ///
+ MEAudioSessionDeviceRemoved = 315,
+
+ ///
+ /// The Windows audio server system was shut down.
+ ///
+ MEAudioSessionServerShutdown = 316,
+
+ ///
+ /// The grouping parameters changed for the audio session.
+ ///
+ MEAudioSessionGroupingParamChanged = 317,
+
+ ///
+ /// The audio session icon changed.
+ ///
+ MEAudioSessionIconChanged = 318,
+
+ ///
+ /// The default audio format for the audio device changed.
+ ///
+ MEAudioSessionFormatChanged = 319,
+
+ ///
+ /// The audio session was disconnected from a Windows Terminal Services session
+ ///
+ MEAudioSessionDisconnected = 320,
+
+ ///
+ /// The audio session was preempted by an exclusive-mode connection.
+ ///
+ MEAudioSessionExclusiveModeOverride = 321,
+ //MESinkV1Anchor = MEAudioSessionExclusiveModeOverride,
+ ///
+ /// Trust Unknown
+ ///
+ METrustUnknown = 400,
+ ///
+ /// The output policy for a stream changed.
+ ///
+ MEPolicyChanged = 401,
+ ///
+ /// Content protection message
+ ///
+ MEContentProtectionMessage = 402,
+ ///
+ /// The IMFOutputTrustAuthority::SetPolicy method completed.
+ ///
+ MEPolicySet = 403,
+ //METrustV1Anchor = MEPolicySet,
+ ///
+ /// DRM License Backup Completed
+ ///
+ MEWMDRMLicenseBackupCompleted = 500,
+ ///
+ /// DRM License Backup Progress
+ ///
+ MEWMDRMLicenseBackupProgress = 501,
+ ///
+ /// DRM License Restore Completed
+ ///
+ MEWMDRMLicenseRestoreCompleted = 502,
+ ///
+ /// DRM License Restore Progress
+ ///
+ MEWMDRMLicenseRestoreProgress = 503,
+ ///
+ /// DRM License Acquisition Completed
+ ///
+ MEWMDRMLicenseAcquisitionCompleted = 506,
+ ///
+ /// DRM Individualization Completed
+ ///
+ MEWMDRMIndividualizationCompleted = 508,
+ ///
+ /// DRM Individualization Progress
+ ///
+ MEWMDRMIndividualizationProgress = 513,
+ ///
+ /// DRM Proximity Completed
+ ///
+ MEWMDRMProximityCompleted = 514,
+ ///
+ /// DRM License Store Cleaned
+ ///
+ MEWMDRMLicenseStoreCleaned = 515,
+ ///
+ /// DRM Revocation Download Completed
+ ///
+ MEWMDRMRevocationDownloadCompleted = 516,
+ //MEWMDRMV1Anchor = MEWMDRMRevocationDownloadCompleted,
+ ///
+ /// Transform Unknown
+ ///
+ METransformUnknown = 600,
+ ///
+ /// Sent by an asynchronous MFT to request a new input sample.
+ ///
+ METransformNeedInput = (METransformUnknown + 1),
+ ///
+ /// Sent by an asynchronous MFT when new output data is available from the MFT.
+ ///
+ METransformHaveOutput = (METransformNeedInput + 1),
+ ///
+ /// Sent by an asynchronous Media Foundation transform (MFT) when a drain operation is complete.
+ ///
+ METransformDrainComplete = (METransformHaveOutput + 1),
+ ///
+ /// Sent by an asynchronous MFT in response to an MFT_MESSAGE_COMMAND_MARKER message.
+ ///
+ METransformMarker = (METransformDrainComplete + 1),
+ //MEReservedMax = 10000
+ }
+}
+
+
+/*
+
+MECaptureAudioSessionDeviceRemoved The device was removed.
+MECaptureAudioSessionDisconnected The audio session is disconnected because the user logged off from a Windows Terminal Services (WTS) session.
+MECaptureAudioSessionExclusiveModeOverride The user opened an audio stream in exclusive mode.
+MECaptureAudioSessionFormatChanged The audio format changed.
+MECaptureAudioSessionServerShutdown The audio session server shutdown.
+MECaptureAudioSessionVolumeChanged The volume changed.
+MEContentProtectionMessage The configuration changed for an output protection scheme.
+MEVideoCaptureDevicePreempted The device has been preempted.
+MEVideoCaptureDeviceRemoved The device has been removed.
+}*/
diff --git a/NAudio/MediaFoundation/MediaFoundationAttributes.cs b/NAudio/MediaFoundation/MediaFoundationAttributes.cs
new file mode 100644
index 00000000..e9124a42
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaFoundationAttributes.cs
@@ -0,0 +1,286 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Media Foundation attribute guids
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms696989%28v=vs.85%29.aspx
+ ///
+ public static class MediaFoundationAttributes
+ {
+ ///
+ /// Specifies whether an MFT performs asynchronous processing.
+ ///
+ public static readonly Guid MF_TRANSFORM_ASYNC = new Guid("f81a699a-649a-497d-8c73-29f8fed6ad7a");
+
+ ///
+ /// Enables the use of an asynchronous MFT.
+ ///
+ public static readonly Guid MF_TRANSFORM_ASYNC_UNLOCK = new Guid("e5666d6b-3422-4eb6-a421-da7db1f8e207");
+
+ ///
+ /// Contains flags for an MFT activation object.
+ ///
+ [FieldDescription("Transform Flags")]
+ public static readonly Guid MF_TRANSFORM_FLAGS_Attribute = new Guid("9359bb7e-6275-46c4-a025-1c01e45f1a86");
+
+ ///
+ /// Specifies the category for an MFT.
+ ///
+ [FieldDescription("Transform Category")]
+ public static readonly Guid MF_TRANSFORM_CATEGORY_Attribute = new Guid("ceabba49-506d-4757-a6ff-66c184987e4e");
+
+ ///
+ /// Contains the class identifier (CLSID) of an MFT.
+ ///
+ [FieldDescription("Class identifier")]
+ public static readonly Guid MFT_TRANSFORM_CLSID_Attribute = new Guid("6821c42b-65a4-4e82-99bc-9a88205ecd0c");
+
+ ///
+ /// Contains the registered input types for a Media Foundation transform (MFT).
+ ///
+ [FieldDescription("Input Types")]
+ public static readonly Guid MFT_INPUT_TYPES_Attributes = new Guid("4276c9b1-759d-4bf3-9cd0-0d723d138f96");
+
+ ///
+ /// Contains the registered output types for a Media Foundation transform (MFT).
+ ///
+ [FieldDescription("Output Types")]
+ public static readonly Guid MFT_OUTPUT_TYPES_Attributes = new Guid("8eae8cf3-a44f-4306-ba5c-bf5dda242818");
+
+ ///
+ /// Contains the symbolic link for a hardware-based MFT.
+ ///
+ public static readonly Guid MFT_ENUM_HARDWARE_URL_Attribute = new Guid("2fb866ac-b078-4942-ab6c-003d05cda674");
+
+ ///
+ /// Contains the display name for a hardware-based MFT.
+ ///
+ [FieldDescription("Name")]
+ public static readonly Guid MFT_FRIENDLY_NAME_Attribute = new Guid("314ffbae-5b41-4c95-9c19-4e7d586face3");
+
+ ///
+ /// Contains a pointer to the stream attributes of the connected stream on a hardware-based MFT.
+ ///
+ public static readonly Guid MFT_CONNECTED_STREAM_ATTRIBUTE = new Guid("71eeb820-a59f-4de2-bcec-38db1dd611a4");
+
+ ///
+ /// Specifies whether a hardware-based MFT is connected to another hardware-based MFT.
+ ///
+ public static readonly Guid MFT_CONNECTED_TO_HW_STREAM = new Guid("34e6e728-06d6-4491-a553-4795650db912");
+
+ ///
+ /// Specifies the preferred output format for an encoder.
+ ///
+ [FieldDescription("Preferred Output Format")]
+ public static readonly Guid MFT_PREFERRED_OUTPUTTYPE_Attribute = new Guid("7e700499-396a-49ee-b1b4-f628021e8c9d");
+
+ ///
+ /// Specifies whether an MFT is registered only in the application's process.
+ ///
+ public static readonly Guid MFT_PROCESS_LOCAL_Attribute = new Guid("543186e4-4649-4e65-b588-4aa352aff379");
+
+ ///
+ /// Contains configuration properties for an encoder.
+ ///
+ public static readonly Guid MFT_PREFERRED_ENCODER_PROFILE = new Guid("53004909-1ef5-46d7-a18e-5a75f8b5905f");
+
+ ///
+ /// Specifies whether a hardware device source uses the system time for time stamps.
+ ///
+ public static readonly Guid MFT_HW_TIMESTAMP_WITH_QPC_Attribute = new Guid("8d030fb8-cc43-4258-a22e-9210bef89be4");
+
+ ///
+ /// Contains an IMFFieldOfUseMFTUnlock pointer, which can be used to unlock the MFT.
+ ///
+ public static readonly Guid MFT_FIELDOFUSE_UNLOCK_Attribute = new Guid("8ec2e9fd-9148-410d-831e-702439461a8e");
+
+ ///
+ /// Contains the merit value of a hardware codec.
+ ///
+ public static readonly Guid MFT_CODEC_MERIT_Attribute = new Guid("88a7cb15-7b07-4a34-9128-e64c6703c4d3");
+
+ ///
+ /// Specifies whether a decoder is optimized for transcoding rather than for playback.
+ ///
+ public static readonly Guid MFT_ENUM_TRANSCODE_ONLY_ATTRIBUTE = new Guid("111ea8cd-b62a-4bdb-89f6-67ffcdc2458b");
+
+ // Presentation descriptor attributes:
+ // http://msdn.microsoft.com/en-gb/library/windows/desktop/aa367736%28v=vs.85%29.aspx
+
+ ///
+ /// Contains a pointer to the proxy object for the application's presentation descriptor.
+ ///
+ [FieldDescription("PMP Host Context")]
+ public static readonly Guid MF_PD_PMPHOST_CONTEXT = new Guid("6c990d31-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Contains a pointer to the presentation descriptor from the protected media path (PMP).
+ ///
+ [FieldDescription("App Context")]
+ public static readonly Guid MF_PD_APP_CONTEXT = new Guid("6c990d32-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Specifies the duration of a presentation, in 100-nanosecond units.
+ ///
+ [FieldDescription("Duration")]
+ public static readonly Guid MF_PD_DURATION = new Guid("6c990d33-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Specifies the total size of the source file, in bytes.
+ ///
+ [FieldDescription("Total File Size")]
+ public static readonly Guid MF_PD_TOTAL_FILE_SIZE = new Guid("6c990d34-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Specifies the audio encoding bit rate for the presentation, in bits per second.
+ ///
+ [FieldDescription("Audio encoding bitrate")]
+ public static readonly Guid MF_PD_AUDIO_ENCODING_BITRATE = new Guid("6c990d35-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Specifies the video encoding bit rate for the presentation, in bits per second.
+ ///
+ [FieldDescription("Video Encoding Bitrate")]
+ public static readonly Guid MF_PD_VIDEO_ENCODING_BITRATE = new Guid("6c990d36-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Specifies the MIME type of the content.
+ ///
+ [FieldDescription("MIME Type")]
+ public static readonly Guid MF_PD_MIME_TYPE = new Guid("6c990d37-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Specifies when a presentation was last modified.
+ ///
+ [FieldDescription("Last Modified Time")]
+ public static readonly Guid MF_PD_LAST_MODIFIED_TIME = new Guid("6c990d38-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// The identifier of the playlist element in the presentation.
+ ///
+ [FieldDescription("Element ID")]
+ public static readonly Guid MF_PD_PLAYBACK_ELEMENT_ID = new Guid("6c990d39-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Contains the preferred RFC 1766 language of the media source.
+ ///
+ [FieldDescription("Preferred Language")]
+ public static readonly Guid MF_PD_PREFERRED_LANGUAGE = new Guid("6c990d3a-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// The time at which the presentation must begin, relative to the start of the media source.
+ ///
+ [FieldDescription("Playback boundary time")]
+ public static readonly Guid MF_PD_PLAYBACK_BOUNDARY_TIME = new Guid("6c990d3b-bb8e-477a-8598-0d5d96fcd88a");
+
+ ///
+ /// Specifies whether the audio streams in the presentation have a variable bit rate.
+ ///
+ [FieldDescription("Audio is variable bitrate")]
+ public static readonly Guid MF_PD_AUDIO_ISVARIABLEBITRATE = new Guid("33026ee0-e387-4582-ae0a-34a2ad3baa18");
+
+ ///
+ /// Media type Major Type
+ ///
+ [FieldDescription("Major Media Type")]
+ public static readonly Guid MF_MT_MAJOR_TYPE = new Guid("48eba18e-f8c9-4687-bf11-0a74c9f96a8f");
+ ///
+ /// Media Type subtype
+ ///
+ [FieldDescription("Media Subtype")]
+ public static readonly Guid MF_MT_SUBTYPE = new Guid("f7e34c9a-42e8-4714-b74b-cb29d72c35e5");
+ ///
+ /// Audio block alignment
+ ///
+ [FieldDescription("Audio block alignment")]
+ public static readonly Guid MF_MT_AUDIO_BLOCK_ALIGNMENT = new Guid("322de230-9eeb-43bd-ab7a-ff412251541d");
+ ///
+ /// Audio average bytes per second
+ ///
+ [FieldDescription("Audio average bytes per second")]
+ public static readonly Guid MF_MT_AUDIO_AVG_BYTES_PER_SECOND = new Guid("1aab75c8-cfef-451c-ab95-ac034b8e1731");
+ ///
+ /// Audio number of channels
+ ///
+ [FieldDescription("Audio number of channels")]
+ public static readonly Guid MF_MT_AUDIO_NUM_CHANNELS = new Guid("37e48bf5-645e-4c5b-89de-ada9e29b696a");
+ ///
+ /// Audio samples per second
+ ///
+ [FieldDescription("Audio samples per second")]
+ public static readonly Guid MF_MT_AUDIO_SAMPLES_PER_SECOND = new Guid("5faeeae7-0290-4c31-9e8a-c534f68d9dba");
+ ///
+ /// Audio bits per sample
+ ///
+ [FieldDescription("Audio bits per sample")]
+ public static readonly Guid MF_MT_AUDIO_BITS_PER_SAMPLE = new Guid("f2deb57f-40fa-4764-aa33-ed4f2d1ff669");
+
+ ///
+ /// Enables the source reader or sink writer to use hardware-based Media Foundation transforms (MFTs).
+ ///
+ [FieldDescription("Enable Hardware Transforms")]
+ public static readonly Guid MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS = new Guid("a634a91c-822b-41b9-a494-4de4643612b0");
+
+ ///
+ /// Contains additional format data for a media type.
+ ///
+ [FieldDescription("User data")]
+ public static readonly Guid MF_MT_USER_DATA = new Guid("b6bc765f-4c3b-40a4-bd51-2535b66fe09d");
+
+ ///
+ /// Specifies for a media type whether each sample is independent of the other samples in the stream.
+ ///
+ [FieldDescription("All samples independent")]
+ public static readonly Guid MF_MT_ALL_SAMPLES_INDEPENDENT = new Guid("c9173739-5e56-461c-b713-46fb995cb95f");
+
+ ///
+ /// Specifies for a media type whether the samples have a fixed size.
+ ///
+ [FieldDescription("Fixed size samples")]
+ public static readonly Guid MF_MT_FIXED_SIZE_SAMPLES = new Guid("b8ebefaf-b718-4e04-b0a9-116775e3321b");
+
+ ///
+ /// Contains a DirectShow format GUID for a media type.
+ ///
+ [FieldDescription("DirectShow Format Guid")]
+ public static readonly Guid MF_MT_AM_FORMAT_TYPE = new Guid("73d1072d-1870-4174-a063-29ff4ff6c11e");
+
+ ///
+ /// Specifies the preferred legacy format structure to use when converting an audio media type.
+ ///
+ [FieldDescription("Preferred legacy format structure")]
+ public static readonly Guid MF_MT_AUDIO_PREFER_WAVEFORMATEX = new Guid("a901aaba-e037-458a-bdf6-545be2074042");
+
+ ///
+ /// Specifies for a media type whether the media data is compressed.
+ ///
+ [FieldDescription("Is Compressed")]
+ public static readonly Guid MF_MT_COMPRESSED = new Guid("3afd0cee-18f2-4ba5-a110-8bea502e1f92");
+
+ ///
+ /// Approximate data rate of the video stream, in bits per second, for a video media type.
+ ///
+ [FieldDescription("Average bitrate")]
+ public static readonly Guid MF_MT_AVG_BITRATE = new Guid("20332624-fb0d-4d9e-bd0d-cbf6786c102e");
+
+ ///
+ /// Specifies the payload type of an Advanced Audio Coding (AAC) stream.
+ /// 0 - The stream contains raw_data_block elements only
+ /// 1 - Audio Data Transport Stream (ADTS). The stream contains an adts_sequence, as defined by MPEG-2.
+ /// 2 - Audio Data Interchange Format (ADIF). The stream contains an adif_sequence, as defined by MPEG-2.
+ /// 3 - The stream contains an MPEG-4 audio transport stream with a synchronization layer (LOAS) and a multiplex layer (LATM).
+ ///
+ [FieldDescription("AAC payload type")]
+ public static readonly Guid MF_MT_AAC_PAYLOAD_TYPE = new Guid("bfbabe79-7434-4d1c-94f0-72a3b9e17188");
+
+ ///
+ /// Specifies the audio profile and level of an Advanced Audio Coding (AAC) stream, as defined by ISO/IEC 14496-3.
+ ///
+ [FieldDescription("AAC Audio Profile Level Indication")]
+ public static readonly Guid MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION = new Guid("7632f0e6-9538-4d61-acda-ea29c8c14456");
+ }
+}
diff --git a/NAudio/MediaFoundation/MediaFoundationErrors.cs b/NAudio/MediaFoundation/MediaFoundationErrors.cs
new file mode 100644
index 00000000..b2a54078
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaFoundationErrors.cs
@@ -0,0 +1,2764 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.MediaFoundation
+{
+ // from mferror.h
+ ///
+ /// Media Foundation Errors
+ ///
+ ///
+ ///
+ /// RANGES
+ /// 14000 - 14999 = General Media Foundation errors
+ /// 15000 - 15999 = ASF parsing errors
+ /// 16000 - 16999 = Media Source errors
+ /// 17000 - 17999 = MEDIAFOUNDATION Network Error Events
+ /// 18000 - 18999 = MEDIAFOUNDATION WMContainer Error Events
+ /// 19000 - 19999 = MEDIAFOUNDATION Media Sink Error Events
+ /// 20000 - 20999 = Renderer errors
+ /// 21000 - 21999 = Topology Errors
+ /// 25000 - 25999 = Timeline Errors
+ /// 26000 - 26999 = Unused
+ /// 28000 - 28999 = Transform errors
+ /// 29000 - 29999 = Content Protection errors
+ /// 40000 - 40999 = Clock errors
+ /// 41000 - 41999 = MF Quality Management Errors
+ /// 42000 - 42999 = MF Transcode API Errors
+ ///
+ public static class MediaFoundationErrors
+ {
+ #region General Media Foundation errors
+ ///
+ /// MessageId: MF_E_PLATFORM_NOT_INITIALIZED
+ ///
+ /// MessageText:
+ ///
+ /// Platform not initialized. Please call MFStartup().%0
+ ///
+ public const int MF_E_PLATFORM_NOT_INITIALIZED = unchecked((int) 0xC00D36B0);
+
+ ///
+ /// MessageId: MF_E_BUFFERTOOSMALL
+ ///
+ /// MessageText:
+ ///
+ /// The buffer was too small to carry out the requested action.%0
+ ///
+ public const int MF_E_BUFFERTOOSMALL = unchecked((int) 0xC00D36B1);
+
+ ///
+ /// MessageId: MF_E_INVALIDREQUEST
+ ///
+ /// MessageText:
+ ///
+ /// The request is invalid in the current state.%0
+ ///
+ public const int MF_E_INVALIDREQUEST = unchecked((int) 0xC00D36B2);
+
+ ///
+ /// MessageId: MF_E_INVALIDSTREAMNUMBER
+ ///
+ /// MessageText:
+ ///
+ /// The stream number provided was invalid.%0
+ ///
+ public const int MF_E_INVALIDSTREAMNUMBER = unchecked((int) 0xC00D36B3);
+
+ ///
+ /// MessageId: MF_E_INVALIDMEDIATYPE
+ ///
+ /// MessageText:
+ ///
+ /// The data specified for the media type is invalid, inconsistent, or not supported by this object.%0
+ ///
+ public const int MF_E_INVALIDMEDIATYPE = unchecked((int) 0xC00D36B4);
+
+ ///
+ /// MessageId: MF_E_NOTACCEPTING
+ ///
+ /// MessageText:
+ ///
+ /// The callee is currently not accepting further input.%0
+ ///
+ public const int MF_E_NOTACCEPTING = unchecked((int) 0xC00D36B5);
+
+ ///
+ /// MessageId: MF_E_NOT_INITIALIZED
+ ///
+ /// MessageText:
+ ///
+ /// This object needs to be initialized before the requested operation can be carried out.%0
+ ///
+ public const int MF_E_NOT_INITIALIZED = unchecked((int) 0xC00D36B6);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_REPRESENTATION
+ ///
+ /// MessageText:
+ ///
+ /// The requested representation is not supported by this object.%0
+ ///
+ public const int MF_E_UNSUPPORTED_REPRESENTATION = unchecked((int) 0xC00D36B7);
+
+ ///
+ /// MessageId: MF_E_NO_MORE_TYPES
+ ///
+ /// MessageText:
+ ///
+ /// An object ran out of media types to suggest therefore the requested chain of streaming objects cannot be completed.%0
+ ///
+ public const int MF_E_NO_MORE_TYPES = unchecked((int) 0xC00D36B9);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_SERVICE
+ ///
+ /// MessageText:
+ ///
+ /// The object does not support the specified service.%0
+ ///
+ public const int MF_E_UNSUPPORTED_SERVICE = unchecked((int) 0xC00D36BA);
+
+ ///
+ /// MessageId: MF_E_UNEXPECTED
+ ///
+ /// MessageText:
+ ///
+ /// An unexpected error has occurred in the operation requested.%0
+ ///
+ public const int MF_E_UNEXPECTED = unchecked((int) 0xC00D36BB);
+
+ ///
+ /// MessageId: MF_E_INVALIDNAME
+ ///
+ /// MessageText:
+ ///
+ /// Invalid name.%0
+ ///
+ public const int MF_E_INVALIDNAME = unchecked((int) 0xC00D36BC);
+
+ ///
+ /// MessageId: MF_E_INVALIDTYPE
+ ///
+ /// MessageText:
+ ///
+ /// Invalid type.%0
+ ///
+ public const int MF_E_INVALIDTYPE = unchecked((int) 0xC00D36BD);
+
+ ///
+ /// MessageId: MF_E_INVALID_FILE_FORMAT
+ ///
+ /// MessageText:
+ ///
+ /// The file does not conform to the relevant file format specification.
+ ///
+ public const int MF_E_INVALID_FILE_FORMAT = unchecked((int) 0xC00D36BE);
+
+ ///
+ /// MessageId: MF_E_INVALIDINDEX
+ ///
+ /// MessageText:
+ ///
+ /// Invalid index.%0
+ ///
+ public const int MF_E_INVALIDINDEX = unchecked((int) 0xC00D36BF);
+
+ ///
+ /// MessageId: MF_E_INVALID_TIMESTAMP
+ ///
+ /// MessageText:
+ ///
+ /// An invalid timestamp was given.%0
+ ///
+ public const int MF_E_INVALID_TIMESTAMP = unchecked((int) 0xC00D36C0);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_SCHEME
+ ///
+ /// MessageText:
+ ///
+ /// The scheme of the given URL is unsupported.%0
+ ///
+ public const int MF_E_UNSUPPORTED_SCHEME = unchecked((int) 0xC00D36C3);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_BYTESTREAM_TYPE
+ ///
+ /// MessageText:
+ ///
+ /// The byte stream type of the given URL is unsupported.%0
+ ///
+ public const int MF_E_UNSUPPORTED_BYTESTREAM_TYPE = unchecked((int) 0xC00D36C4);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_TIME_FORMAT
+ ///
+ /// MessageText:
+ ///
+ /// The given time format is unsupported.%0
+ ///
+ public const int MF_E_UNSUPPORTED_TIME_FORMAT = unchecked((int) 0xC00D36C5);
+
+ ///
+ /// MessageId: MF_E_NO_SAMPLE_TIMESTAMP
+ ///
+ /// MessageText:
+ ///
+ /// The Media Sample does not have a timestamp.%0
+ ///
+ public const int MF_E_NO_SAMPLE_TIMESTAMP = unchecked((int) 0xC00D36C8);
+
+ ///
+ /// MessageId: MF_E_NO_SAMPLE_DURATION
+ ///
+ /// MessageText:
+ ///
+ /// The Media Sample does not have a duration.%0
+ ///
+ public const int MF_E_NO_SAMPLE_DURATION = unchecked((int) 0xC00D36C9);
+
+ ///
+ /// MessageId: MF_E_INVALID_STREAM_DATA
+ ///
+ /// MessageText:
+ ///
+ /// The request failed because the data in the stream is corrupt.%0\n.
+ ///
+ public const int MF_E_INVALID_STREAM_DATA = unchecked((int) 0xC00D36CB);
+
+ ///
+ /// MessageId: MF_E_RT_UNAVAILABLE
+ ///
+ /// MessageText:
+ ///
+ /// Real time services are not available.%0
+ ///
+ public const int MF_E_RT_UNAVAILABLE = unchecked((int) 0xC00D36CF);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_RATE
+ ///
+ /// MessageText:
+ ///
+ /// The specified rate is not supported.%0
+ ///
+ public const int MF_E_UNSUPPORTED_RATE = unchecked((int) 0xC00D36D0);
+
+ ///
+ /// MessageId: MF_E_THINNING_UNSUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// This component does not support stream-thinning.%0
+ ///
+ public const int MF_E_THINNING_UNSUPPORTED = unchecked((int) 0xC00D36D1);
+
+ ///
+ /// MessageId: MF_E_REVERSE_UNSUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The call failed because no reverse playback rates are available.%0
+ ///
+ public const int MF_E_REVERSE_UNSUPPORTED = unchecked((int) 0xC00D36D2);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_RATE_TRANSITION
+ ///
+ /// MessageText:
+ ///
+ /// The requested rate transition cannot occur in the current state.%0
+ ///
+ public const int MF_E_UNSUPPORTED_RATE_TRANSITION = unchecked((int) 0xC00D36D3);
+
+ ///
+ /// MessageId: MF_E_RATE_CHANGE_PREEMPTED
+ ///
+ /// MessageText:
+ ///
+ /// The requested rate change has been pre-empted and will not occur.%0
+ ///
+ public const int MF_E_RATE_CHANGE_PREEMPTED = unchecked((int) 0xC00D36D4);
+
+ ///
+ /// MessageId: MF_E_NOT_FOUND
+ ///
+ /// MessageText:
+ ///
+ /// The specified object or value does not exist.%0
+ ///
+ public const int MF_E_NOT_FOUND = unchecked((int) 0xC00D36D5);
+
+ ///
+ /// MessageId: MF_E_NOT_AVAILABLE
+ ///
+ /// MessageText:
+ ///
+ /// The requested value is not available.%0
+ ///
+ public const int MF_E_NOT_AVAILABLE = unchecked((int) 0xC00D36D6);
+
+ ///
+ /// MessageId: MF_E_NO_CLOCK
+ ///
+ /// MessageText:
+ ///
+ /// The specified operation requires a clock and no clock is available.%0
+ ///
+ public const int MF_E_NO_CLOCK = unchecked((int) 0xC00D36D7);
+
+ ///
+ /// MessageId: MF_S_MULTIPLE_BEGIN
+ ///
+ /// MessageText:
+ ///
+ /// This callback and state had already been passed in to this event generator earlier.%0
+ ///
+ public const int MF_S_MULTIPLE_BEGIN = unchecked((int) 0x000D36D8);
+
+ ///
+ /// MessageId: MF_E_MULTIPLE_BEGIN
+ ///
+ /// MessageText:
+ ///
+ /// This callback has already been passed in to this event generator.%0
+ ///
+ public const int MF_E_MULTIPLE_BEGIN = unchecked((int) 0xC00D36D9);
+
+ ///
+ /// MessageId: MF_E_MULTIPLE_SUBSCRIBERS
+ ///
+ /// MessageText:
+ ///
+ /// Some component is already listening to events on this event generator.%0
+ ///
+ public const int MF_E_MULTIPLE_SUBSCRIBERS = unchecked((int) 0xC00D36DA);
+
+ ///
+ /// MessageId: MF_E_TIMER_ORPHANED
+ ///
+ /// MessageText:
+ ///
+ /// This timer was orphaned before its callback time arrived.%0
+ ///
+ public const int MF_E_TIMER_ORPHANED = unchecked((int) 0xC00D36DB);
+
+ ///
+ /// MessageId: MF_E_STATE_TRANSITION_PENDING
+ ///
+ /// MessageText:
+ ///
+ /// A state transition is already pending.%0
+ ///
+ public const int MF_E_STATE_TRANSITION_PENDING = unchecked((int) 0xC00D36DC);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_STATE_TRANSITION
+ ///
+ /// MessageText:
+ ///
+ /// The requested state transition is unsupported.%0
+ ///
+ public const int MF_E_UNSUPPORTED_STATE_TRANSITION = unchecked((int) 0xC00D36DD);
+
+ ///
+ /// MessageId: MF_E_UNRECOVERABLE_ERROR_OCCURRED
+ ///
+ /// MessageText:
+ ///
+ /// An unrecoverable error has occurred.%0
+ ///
+ public const int MF_E_UNRECOVERABLE_ERROR_OCCURRED = unchecked((int) 0xC00D36DE);
+
+ ///
+ /// MessageId: MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS
+ ///
+ /// MessageText:
+ ///
+ /// The provided sample has too many buffers.%0
+ ///
+ public const int MF_E_SAMPLE_HAS_TOO_MANY_BUFFERS = unchecked((int) 0xC00D36DF);
+
+ ///
+ /// MessageId: MF_E_SAMPLE_NOT_WRITABLE
+ ///
+ /// MessageText:
+ ///
+ /// The provided sample is not writable.%0
+ ///
+ public const int MF_E_SAMPLE_NOT_WRITABLE = unchecked((int) 0xC00D36E0);
+
+ ///
+ /// MessageId: MF_E_INVALID_KEY
+ ///
+ /// MessageText:
+ ///
+ /// The specified key is not valid.
+ ///
+ public const int MF_E_INVALID_KEY = unchecked((int) 0xC00D36E2);
+
+ ///
+ /// MessageId: MF_E_BAD_STARTUP_VERSION
+ ///
+ /// MessageText:
+ ///
+ /// You are calling MFStartup with the wrong MF_VERSION. Mismatched bits?
+ ///
+ public const int MF_E_BAD_STARTUP_VERSION = unchecked((int) 0xC00D36E3);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_CAPTION
+ ///
+ /// MessageText:
+ ///
+ /// The caption of the given URL is unsupported.%0
+ ///
+ public const int MF_E_UNSUPPORTED_CAPTION = unchecked((int) 0xC00D36E4);
+
+ ///
+ /// MessageId: MF_E_INVALID_POSITION
+ ///
+ /// MessageText:
+ ///
+ /// The operation on the current offset is not permitted.%0
+ ///
+ public const int MF_E_INVALID_POSITION = unchecked((int) 0xC00D36E5);
+
+ ///
+ /// MessageId: MF_E_ATTRIBUTENOTFOUND
+ ///
+ /// MessageText:
+ ///
+ /// The requested attribute was not found.%0
+ ///
+ public const int MF_E_ATTRIBUTENOTFOUND = unchecked((int) 0xC00D36E6);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_TYPE_NOT_ALLOWED
+ ///
+ /// MessageText:
+ ///
+ /// The specified property type is not allowed in this context.%0
+ ///
+ public const int MF_E_PROPERTY_TYPE_NOT_ALLOWED = unchecked((int) 0xC00D36E7);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_TYPE_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The specified property type is not supported.%0
+ ///
+ public const int MF_E_PROPERTY_TYPE_NOT_SUPPORTED = unchecked((int) 0xC00D36E8);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_EMPTY
+ ///
+ /// MessageText:
+ ///
+ /// The specified property is empty.%0
+ ///
+ public const int MF_E_PROPERTY_EMPTY = unchecked((int) 0xC00D36E9);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_NOT_EMPTY
+ ///
+ /// MessageText:
+ ///
+ /// The specified property is not empty.%0
+ ///
+ public const int MF_E_PROPERTY_NOT_EMPTY = unchecked((int) 0xC00D36EA);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_VECTOR_NOT_ALLOWED
+ ///
+ /// MessageText:
+ ///
+ /// The vector property specified is not allowed in this context.%0
+ ///
+ public const int MF_E_PROPERTY_VECTOR_NOT_ALLOWED = unchecked((int) 0xC00D36EB);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_VECTOR_REQUIRED
+ ///
+ /// MessageText:
+ ///
+ /// A vector property is required in this context.%0
+ ///
+ public const int MF_E_PROPERTY_VECTOR_REQUIRED = unchecked((int) 0xC00D36EC);
+
+ ///
+ /// MessageId: MF_E_OPERATION_CANCELLED
+ ///
+ /// MessageText:
+ ///
+ /// The operation is cancelled.%0
+ ///
+ public const int MF_E_OPERATION_CANCELLED = unchecked((int) 0xC00D36ED);
+
+ ///
+ /// MessageId: MF_E_BYTESTREAM_NOT_SEEKABLE
+ ///
+ /// MessageText:
+ ///
+ /// The provided bytestream was expected to be seekable and it is not.%0
+ ///
+ public const int MF_E_BYTESTREAM_NOT_SEEKABLE = unchecked((int) 0xC00D36EE);
+
+ ///
+ /// MessageId: MF_E_DISABLED_IN_SAFEMODE
+ ///
+ /// MessageText:
+ ///
+ /// The Media Foundation platform is disabled when the system is running in Safe Mode.%0
+ ///
+ public const int MF_E_DISABLED_IN_SAFEMODE = unchecked((int) 0xC00D36EF);
+
+ ///
+ /// MessageId: MF_E_CANNOT_PARSE_BYTESTREAM
+ ///
+ /// MessageText:
+ ///
+ /// The Media Source could not parse the byte stream.%0
+ ///
+ public const int MF_E_CANNOT_PARSE_BYTESTREAM = unchecked((int) 0xC00D36F0);
+
+ ///
+ /// MessageId: MF_E_SOURCERESOLVER_MUTUALLY_EXCLUSIVE_FLAGS
+ ///
+ /// MessageText:
+ ///
+ /// Mutually exclusive flags have been specified to source resolver. This flag combination is invalid.%0
+ ///
+ public const int MF_E_SOURCERESOLVER_MUTUALLY_EXCLUSIVE_FLAGS = unchecked((int) 0xC00D36F1);
+
+ ///
+ /// MessageId: MF_E_MEDIAPROC_WRONGSTATE
+ ///
+ /// MessageText:
+ ///
+ /// MediaProc is in the wrong state%0
+ ///
+ public const int MF_E_MEDIAPROC_WRONGSTATE = unchecked((int) 0xC00D36F2);
+
+ ///
+ /// MessageId: MF_E_RT_THROUGHPUT_NOT_AVAILABLE
+ ///
+ /// MessageText:
+ ///
+ /// Real time I/O service can not provide requested throughput.%0
+ ///
+ public const int MF_E_RT_THROUGHPUT_NOT_AVAILABLE = unchecked((int) 0xC00D36F3);
+
+ ///
+ /// MessageId: MF_E_RT_TOO_MANY_CLASSES
+ ///
+ /// MessageText:
+ ///
+ /// The workqueue cannot be registered with more classes.%0
+ ///
+ public const int MF_E_RT_TOO_MANY_CLASSES = unchecked((int) 0xC00D36F4);
+
+ ///
+ /// MessageId: MF_E_RT_WOULDBLOCK
+ ///
+ /// MessageText:
+ ///
+ /// This operation cannot succeed because another thread owns this object.%0
+ ///
+ public const int MF_E_RT_WOULDBLOCK = unchecked((int) 0xC00D36F5);
+
+ ///
+ /// MessageId: MF_E_NO_BITPUMP
+ ///
+ /// MessageText:
+ ///
+ /// Internal. Bitpump not found.%0
+ ///
+ public const int MF_E_NO_BITPUMP = unchecked((int) 0xC00D36F6);
+
+ ///
+ /// MessageId: MF_E_RT_OUTOFMEMORY
+ ///
+ /// MessageText:
+ ///
+ /// No more RT memory available.%0
+ ///
+ public const int MF_E_RT_OUTOFMEMORY = unchecked((int) 0xC00D36F7);
+
+ ///
+ /// MessageId: MF_E_RT_WORKQUEUE_CLASS_NOT_SPECIFIED
+ ///
+ /// MessageText:
+ ///
+ /// An MMCSS class has not been set for this work queue.%0
+ ///
+ public const int MF_E_RT_WORKQUEUE_CLASS_NOT_SPECIFIED = unchecked((int) 0xC00D36F8);
+
+ ///
+ /// MessageId: MF_E_INSUFFICIENT_BUFFER
+ ///
+ /// MessageText:
+ ///
+ /// Insufficient memory for response.%0
+ ///
+ public const int MF_E_INSUFFICIENT_BUFFER = unchecked((int) 0xC00D7170);
+
+ ///
+ /// MessageId: MF_E_CANNOT_CREATE_SINK
+ ///
+ /// MessageText:
+ ///
+ /// Activate failed to create mediasink. Call OutputNode::GetUINT32(MF_TOPONODE_MAJORTYPE) for more information. %0
+ ///
+ public const int MF_E_CANNOT_CREATE_SINK = unchecked((int) 0xC00D36FA);
+
+ ///
+ /// MessageId: MF_E_BYTESTREAM_UNKNOWN_LENGTH
+ ///
+ /// MessageText:
+ ///
+ /// The length of the provided bytestream is unknown.%0
+ ///
+ public const int MF_E_BYTESTREAM_UNKNOWN_LENGTH = unchecked((int) 0xC00D36FB);
+
+ ///
+ /// MessageId: MF_E_SESSION_PAUSEWHILESTOPPED
+ ///
+ /// MessageText:
+ ///
+ /// The media session cannot pause from a stopped state.%0
+ ///
+ public const int MF_E_SESSION_PAUSEWHILESTOPPED = unchecked((int) 0xC00D36FC);
+
+ ///
+ /// MessageId: MF_S_ACTIVATE_REPLACED
+ ///
+ /// MessageText:
+ ///
+ /// The activate could not be created in the remote process for some reason it was replaced with empty one.%0
+ ///
+ public const int MF_S_ACTIVATE_REPLACED = unchecked((int) 0x000D36FD);
+
+ ///
+ /// MessageId: MF_E_FORMAT_CHANGE_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The data specified for the media type is supported, but would require a format change, which is not supported by this object.%0
+ ///
+ public const int MF_E_FORMAT_CHANGE_NOT_SUPPORTED = unchecked((int) 0xC00D36FE);
+
+ ///
+ /// MessageId: MF_E_INVALID_WORKQUEUE
+ ///
+ /// MessageText:
+ ///
+ /// The operation failed because an invalid combination of workqueue ID and flags was specified.%0
+ ///
+ public const int MF_E_INVALID_WORKQUEUE = unchecked((int) 0xC00D36FF);
+
+ ///
+ /// MessageId: MF_E_DRM_UNSUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// No DRM support is available.%0
+ ///
+ public const int MF_E_DRM_UNSUPPORTED = unchecked((int) 0xC00D3700);
+
+ ///
+ /// MessageId: MF_E_UNAUTHORIZED
+ ///
+ /// MessageText:
+ ///
+ /// This operation is not authorized.%0
+ ///
+ public const int MF_E_UNAUTHORIZED = unchecked((int) 0xC00D3701);
+
+ ///
+ /// MessageId: MF_E_OUT_OF_RANGE
+ ///
+ /// MessageText:
+ ///
+ /// The value is not in the specified or valid range.%0
+ ///
+ public const int MF_E_OUT_OF_RANGE = unchecked((int) 0xC00D3702);
+
+ ///
+ /// MessageId: MF_E_INVALID_CODEC_MERIT
+ ///
+ /// MessageText:
+ ///
+ /// The registered codec merit is not valid.%0
+ ///
+ public const int MF_E_INVALID_CODEC_MERIT = unchecked((int) 0xC00D3703);
+
+ ///
+ /// MessageId: MF_E_HW_MFT_FAILED_START_STREAMING
+ ///
+ /// MessageText:
+ ///
+ /// Hardware MFT failed to start streaming due to lack of hardware resources.%0
+ ///
+ public const int MF_E_HW_MFT_FAILED_START_STREAMING = unchecked((int) 0xC00D3704);
+
+ #endregion
+ #region MEDIAFOUNDATION ASF Parsing Informational Events
+
+ ///
+ /// MessageId: MF_S_ASF_PARSEINPROGRESS
+ ///
+ /// MessageText:
+ ///
+ /// Parsing is still in progress and is not yet complete.%0
+ ///
+ public const int MF_S_ASF_PARSEINPROGRESS = unchecked((int) 0x400D3A98);
+ #endregion
+
+ #region MEDIAFOUNDATION ASF Parsing Error Events
+
+ ///
+ /// MessageId: MF_E_ASF_PARSINGINCOMPLETE
+ ///
+ /// MessageText:
+ ///
+ /// Not enough data have been parsed to carry out the requested action.%0
+ ///
+ public const int MF_E_ASF_PARSINGINCOMPLETE = unchecked((int) 0xC00D3A98);
+
+ ///
+ /// MessageId: MF_E_ASF_MISSINGDATA
+ ///
+ /// MessageText:
+ ///
+ /// There is a gap in the ASF data provided.%0
+ ///
+ public const int MF_E_ASF_MISSINGDATA = unchecked((int) 0xC00D3A99);
+
+ ///
+ /// MessageId: MF_E_ASF_INVALIDDATA
+ ///
+ /// MessageText:
+ ///
+ /// The data provided are not valid ASF.%0
+ ///
+ public const int MF_E_ASF_INVALIDDATA = unchecked((int) 0xC00D3A9A);
+
+ ///
+ /// MessageId: MF_E_ASF_OPAQUEPACKET
+ ///
+ /// MessageText:
+ ///
+ /// The packet is opaque, so the requested information cannot be returned.%0
+ ///
+ public const int MF_E_ASF_OPAQUEPACKET = unchecked((int) 0xC00D3A9B);
+
+ ///
+ /// MessageId: MF_E_ASF_NOINDEX
+ ///
+ /// MessageText:
+ ///
+ /// The requested operation failed since there is no appropriate ASF index.%0
+ ///
+ public const int MF_E_ASF_NOINDEX = unchecked((int) 0xC00D3A9C);
+
+ ///
+ /// MessageId: MF_E_ASF_OUTOFRANGE
+ ///
+ /// MessageText:
+ ///
+ /// The value supplied is out of range for this operation.%0
+ ///
+ public const int MF_E_ASF_OUTOFRANGE = unchecked((int) 0xC00D3A9D);
+
+ ///
+ /// MessageId: MF_E_ASF_INDEXNOTLOADED
+ ///
+ /// MessageText:
+ ///
+ /// The index entry requested needs to be loaded before it can be available.%0
+ ///
+ public const int MF_E_ASF_INDEXNOTLOADED = unchecked((int) 0xC00D3A9E);
+
+ ///
+ /// MessageId: MF_E_ASF_TOO_MANY_PAYLOADS
+ ///
+ /// MessageText:
+ ///
+ /// The packet has reached the maximum number of payloads.%0
+ ///
+ public const int MF_E_ASF_TOO_MANY_PAYLOADS = unchecked((int) 0xC00D3A9F);
+
+ ///
+ /// MessageId: MF_E_ASF_UNSUPPORTED_STREAM_TYPE
+ ///
+ /// MessageText:
+ ///
+ /// Stream type is not supported.%0
+ ///
+ public const int MF_E_ASF_UNSUPPORTED_STREAM_TYPE = unchecked((int) 0xC00D3AA0);
+
+ ///
+ /// MessageId: MF_E_ASF_DROPPED_PACKET
+ ///
+ /// MessageText:
+ ///
+ /// One or more ASF packets were dropped.%0
+ ///
+ public const int MF_E_ASF_DROPPED_PACKET = unchecked((int) 0xC00D3AA1);
+
+ #endregion
+ #region MEDIAFOUNDATION Media Source Error Events
+
+ ///
+ /// MessageId: MF_E_NO_EVENTS_AVAILABLE
+ ///
+ /// MessageText:
+ ///
+ /// There are no events available in the queue.%0
+ ///
+ public const int MF_E_NO_EVENTS_AVAILABLE = unchecked((int) 0xC00D3E80);
+
+ ///
+ /// MessageId: MF_E_INVALID_STATE_TRANSITION
+ ///
+ /// MessageText:
+ ///
+ /// A media source cannot go from the stopped state to the paused state.%0
+ ///
+ public const int MF_E_INVALID_STATE_TRANSITION = unchecked((int) 0xC00D3E82);
+
+ ///
+ /// MessageId: MF_E_END_OF_STREAM
+ ///
+ /// MessageText:
+ ///
+ /// The media stream cannot process any more samples because there are no more samples in the stream.%0
+ ///
+ public const int MF_E_END_OF_STREAM = unchecked((int) 0xC00D3E84);
+
+ ///
+ /// MessageId: MF_E_SHUTDOWN
+ ///
+ /// MessageText:
+ ///
+ /// The request is invalid because Shutdown() has been called.%0
+ ///
+ public const int MF_E_SHUTDOWN = unchecked((int) 0xC00D3E85);
+
+ ///
+ /// MessageId: MF_E_MP3_NOTFOUND
+ ///
+ /// MessageText:
+ ///
+ /// The MP3 object was not found.%0
+ ///
+ public const int MF_E_MP3_NOTFOUND = unchecked((int) 0xC00D3E86);
+
+ ///
+ /// MessageId: MF_E_MP3_OUTOFDATA
+ ///
+ /// MessageText:
+ ///
+ /// The MP3 parser ran out of data before finding the MP3 object.%0
+ ///
+ public const int MF_E_MP3_OUTOFDATA = unchecked((int) 0xC00D3E87);
+
+ ///
+ /// MessageId: MF_E_MP3_NOTMP3
+ ///
+ /// MessageText:
+ ///
+ /// The file is not really a MP3 file.%0
+ ///
+ public const int MF_E_MP3_NOTMP3 = unchecked((int) 0xC00D3E88);
+
+ ///
+ /// MessageId: MF_E_MP3_NOTSUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The MP3 file is not supported.%0
+ ///
+ public const int MF_E_MP3_NOTSUPPORTED = unchecked((int) 0xC00D3E89);
+
+ ///
+ /// MessageId: MF_E_NO_DURATION
+ ///
+ /// MessageText:
+ ///
+ /// The Media stream has no duration.%0
+ ///
+ public const int MF_E_NO_DURATION = unchecked((int) 0xC00D3E8A);
+
+ ///
+ /// MessageId: MF_E_INVALID_FORMAT
+ ///
+ /// MessageText:
+ ///
+ /// The Media format is recognized but is invalid.%0
+ ///
+ public const int MF_E_INVALID_FORMAT = unchecked((int) 0xC00D3E8C);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_NOT_FOUND
+ ///
+ /// MessageText:
+ ///
+ /// The property requested was not found.%0
+ ///
+ public const int MF_E_PROPERTY_NOT_FOUND = unchecked((int) 0xC00D3E8D);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_READ_ONLY
+ ///
+ /// MessageText:
+ ///
+ /// The property is read only.%0
+ ///
+ public const int MF_E_PROPERTY_READ_ONLY = unchecked((int) 0xC00D3E8E);
+
+ ///
+ /// MessageId: MF_E_PROPERTY_NOT_ALLOWED
+ ///
+ /// MessageText:
+ ///
+ /// The specified property is not allowed in this context.%0
+ ///
+ public const int MF_E_PROPERTY_NOT_ALLOWED = unchecked((int) 0xC00D3E8F);
+
+ ///
+ /// MessageId: MF_E_MEDIA_SOURCE_NOT_STARTED
+ ///
+ /// MessageText:
+ ///
+ /// The media source is not started.%0
+ ///
+ public const int MF_E_MEDIA_SOURCE_NOT_STARTED = unchecked((int) 0xC00D3E91);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_FORMAT
+ ///
+ /// MessageText:
+ ///
+ /// The Media format is recognized but not supported.%0
+ ///
+ public const int MF_E_UNSUPPORTED_FORMAT = unchecked((int) 0xC00D3E98);
+
+ ///
+ /// MessageId: MF_E_MP3_BAD_CRC
+ ///
+ /// MessageText:
+ ///
+ /// The MPEG frame has bad CRC.%0
+ ///
+ public const int MF_E_MP3_BAD_CRC = unchecked((int) 0xC00D3E99);
+
+ ///
+ /// MessageId: MF_E_NOT_PROTECTED
+ ///
+ /// MessageText:
+ ///
+ /// The file is not protected.%0
+ ///
+ public const int MF_E_NOT_PROTECTED = unchecked((int) 0xC00D3E9A);
+
+ ///
+ /// MessageId: MF_E_MEDIA_SOURCE_WRONGSTATE
+ ///
+ /// MessageText:
+ ///
+ /// The media source is in the wrong state%0
+ ///
+ public const int MF_E_MEDIA_SOURCE_WRONGSTATE = unchecked((int) 0xC00D3E9B);
+
+ ///
+ /// MessageId: MF_E_MEDIA_SOURCE_NO_STREAMS_SELECTED
+ ///
+ /// MessageText:
+ ///
+ /// No streams are selected in source presentation descriptor.%0
+ ///
+ public const int MF_E_MEDIA_SOURCE_NO_STREAMS_SELECTED = unchecked((int) 0xC00D3E9C);
+
+ ///
+ /// MessageId: MF_E_CANNOT_FIND_KEYFRAME_SAMPLE
+ ///
+ /// MessageText:
+ ///
+ /// No key frame sample was found.%0
+ ///
+ public const int MF_E_CANNOT_FIND_KEYFRAME_SAMPLE = unchecked((int) 0xC00D3E9D);
+
+ #endregion
+ #region MEDIAFOUNDATION Network Error Events
+
+ ///
+ /// MessageId: MF_E_NETWORK_RESOURCE_FAILURE
+ ///
+ /// MessageText:
+ ///
+ /// An attempt to acquire a network resource failed.%0
+ ///
+ public const int MF_E_NETWORK_RESOURCE_FAILURE = unchecked((int) 0xC00D4268);
+
+ ///
+ /// MessageId: MF_E_NET_WRITE
+ ///
+ /// MessageText:
+ ///
+ /// Error writing to the network.%0
+ ///
+ public const int MF_E_NET_WRITE = unchecked((int) 0xC00D4269);
+
+ ///
+ /// MessageId: MF_E_NET_READ
+ ///
+ /// MessageText:
+ ///
+ /// Error reading from the network.%0
+ ///
+ public const int MF_E_NET_READ = unchecked((int) 0xC00D426A);
+
+ ///
+ /// MessageId: MF_E_NET_REQUIRE_NETWORK
+ ///
+ /// MessageText:
+ ///
+ /// Internal. Entry cannot complete operation without network.%0
+ ///
+ public const int MF_E_NET_REQUIRE_NETWORK = unchecked((int) 0xC00D426B);
+
+ ///
+ /// MessageId: MF_E_NET_REQUIRE_ASYNC
+ ///
+ /// MessageText:
+ ///
+ /// Internal. Async op is required.%0
+ ///
+ public const int MF_E_NET_REQUIRE_ASYNC = unchecked((int) 0xC00D426C);
+
+ ///
+ /// MessageId: MF_E_NET_BWLEVEL_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// Internal. Bandwidth levels are not supported.%0
+ ///
+ public const int MF_E_NET_BWLEVEL_NOT_SUPPORTED = unchecked((int) 0xC00D426D);
+
+ ///
+ /// MessageId: MF_E_NET_STREAMGROUPS_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// Internal. Stream groups are not supported.%0
+ ///
+ public const int MF_E_NET_STREAMGROUPS_NOT_SUPPORTED = unchecked((int) 0xC00D426E);
+
+ ///
+ /// MessageId: MF_E_NET_MANUALSS_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// Manual stream selection is not supported.%0
+ ///
+ public const int MF_E_NET_MANUALSS_NOT_SUPPORTED = unchecked((int) 0xC00D426F);
+
+ ///
+ /// MessageId: MF_E_NET_INVALID_PRESENTATION_DESCRIPTOR
+ ///
+ /// MessageText:
+ ///
+ /// Invalid presentation descriptor.%0
+ ///
+ public const int MF_E_NET_INVALID_PRESENTATION_DESCRIPTOR = unchecked((int) 0xC00D4270);
+
+ ///
+ /// MessageId: MF_E_NET_CACHESTREAM_NOT_FOUND
+ ///
+ /// MessageText:
+ ///
+ /// Cannot find cache stream.%0
+ ///
+ public const int MF_E_NET_CACHESTREAM_NOT_FOUND = unchecked((int) 0xC00D4271);
+
+ ///
+ /// MessageId: MF_I_MANUAL_PROXY
+ ///
+ /// MessageText:
+ ///
+ /// The proxy setting is manual.%0
+ ///
+ public const int MF_I_MANUAL_PROXY = unchecked((int) 0x400D4272);
+
+ ///duplicate removed
+ ///MessageId=17011 Severity=Informational Facility=MEDIAFOUNDATION SymbolicName=MF_E_INVALID_REQUEST
+ ///Language=English
+ ///The request is invalid in the current state.%0
+ ///.
+ ///
+ /// MessageId: MF_E_NET_REQUIRE_INPUT
+ ///
+ /// MessageText:
+ ///
+ /// Internal. Entry cannot complete operation without input.%0
+ ///
+ public const int MF_E_NET_REQUIRE_INPUT = unchecked((int) 0xC00D4274);
+
+ ///
+ /// MessageId: MF_E_NET_REDIRECT
+ ///
+ /// MessageText:
+ ///
+ /// The client redirected to another server.%0
+ ///
+ public const int MF_E_NET_REDIRECT = unchecked((int) 0xC00D4275);
+
+ ///
+ /// MessageId: MF_E_NET_REDIRECT_TO_PROXY
+ ///
+ /// MessageText:
+ ///
+ /// The client is redirected to a proxy server.%0
+ ///
+ public const int MF_E_NET_REDIRECT_TO_PROXY = unchecked((int) 0xC00D4276);
+
+ ///
+ /// MessageId: MF_E_NET_TOO_MANY_REDIRECTS
+ ///
+ /// MessageText:
+ ///
+ /// The client reached maximum redirection limit.%0
+ ///
+ public const int MF_E_NET_TOO_MANY_REDIRECTS = unchecked((int) 0xC00D4277);
+
+ ///
+ /// MessageId: MF_E_NET_TIMEOUT
+ ///
+ /// MessageText:
+ ///
+ /// The server, a computer set up to offer multimedia content to other computers, could not handle your request for multimedia content in a timely manner. Please try again later.%0
+ ///
+ public const int MF_E_NET_TIMEOUT = unchecked((int) 0xC00D4278);
+
+ ///
+ /// MessageId: MF_E_NET_CLIENT_CLOSE
+ ///
+ /// MessageText:
+ ///
+ /// The control socket is closed by the client.%0
+ ///
+ public const int MF_E_NET_CLIENT_CLOSE = unchecked((int) 0xC00D4279);
+
+ ///
+ /// MessageId: MF_E_NET_BAD_CONTROL_DATA
+ ///
+ /// MessageText:
+ ///
+ /// The server received invalid data from the client on the control connection.%0
+ ///
+ public const int MF_E_NET_BAD_CONTROL_DATA = unchecked((int) 0xC00D427A);
+
+ ///
+ /// MessageId: MF_E_NET_INCOMPATIBLE_SERVER
+ ///
+ /// MessageText:
+ ///
+ /// The server is not a compatible streaming media server.%0
+ ///
+ public const int MF_E_NET_INCOMPATIBLE_SERVER = unchecked((int) 0xC00D427B);
+
+ ///
+ /// MessageId: MF_E_NET_UNSAFE_URL
+ ///
+ /// MessageText:
+ ///
+ /// Url.%0
+ ///
+ public const int MF_E_NET_UNSAFE_URL = unchecked((int) 0xC00D427C);
+
+ ///
+ /// MessageId: MF_E_NET_CACHE_NO_DATA
+ ///
+ /// MessageText:
+ ///
+ /// Data is not available.%0
+ ///
+ public const int MF_E_NET_CACHE_NO_DATA = unchecked((int) 0xC00D427D);
+
+ ///
+ /// MessageId: MF_E_NET_EOL
+ ///
+ /// MessageText:
+ ///
+ /// End of line.%0
+ ///
+ public const int MF_E_NET_EOL = unchecked((int) 0xC00D427E);
+
+ ///
+ /// MessageId: MF_E_NET_BAD_REQUEST
+ ///
+ /// MessageText:
+ ///
+ /// The request could not be understood by the server.%0
+ ///
+ public const int MF_E_NET_BAD_REQUEST = unchecked((int) 0xC00D427F);
+
+ ///
+ /// MessageId: MF_E_NET_INTERNAL_SERVER_ERROR
+ ///
+ /// MessageText:
+ ///
+ /// The server encountered an unexpected condition which prevented it from fulfilling the request.%0
+ ///
+ public const int MF_E_NET_INTERNAL_SERVER_ERROR = unchecked((int) 0xC00D4280);
+
+ ///
+ /// MessageId: MF_E_NET_SESSION_NOT_FOUND
+ ///
+ /// MessageText:
+ ///
+ /// Session not found.%0
+ ///
+ public const int MF_E_NET_SESSION_NOT_FOUND = unchecked((int) 0xC00D4281);
+
+ ///
+ /// MessageId: MF_E_NET_NOCONNECTION
+ ///
+ /// MessageText:
+ ///
+ /// There is no connection established with the Windows Media server. The operation failed.%0
+ ///
+ public const int MF_E_NET_NOCONNECTION = unchecked((int) 0xC00D4282);
+
+ ///
+ /// MessageId: MF_E_NET_CONNECTION_FAILURE
+ ///
+ /// MessageText:
+ ///
+ /// The network connection has failed.%0
+ ///
+ public const int MF_E_NET_CONNECTION_FAILURE = unchecked((int) 0xC00D4283);
+
+ ///
+ /// MessageId: MF_E_NET_INCOMPATIBLE_PUSHSERVER
+ ///
+ /// MessageText:
+ ///
+ /// The Server service that received the HTTP push request is not a compatible version of Windows Media Services (WMS). This error may indicate the push request was received by IIS instead of WMS. Ensure WMS is started and has the HTTP Server control protocol properly enabled and try again.%0
+ ///
+ public const int MF_E_NET_INCOMPATIBLE_PUSHSERVER = unchecked((int) 0xC00D4284);
+
+ ///
+ /// MessageId: MF_E_NET_SERVER_ACCESSDENIED
+ ///
+ /// MessageText:
+ ///
+ /// The Windows Media server is denying access. The username and/or password might be incorrect.%0
+ ///
+ public const int MF_E_NET_SERVER_ACCESSDENIED = unchecked((int) 0xC00D4285);
+
+ ///
+ /// MessageId: MF_E_NET_PROXY_ACCESSDENIED
+ ///
+ /// MessageText:
+ ///
+ /// The proxy server is denying access. The username and/or password might be incorrect.%0
+ ///
+ public const int MF_E_NET_PROXY_ACCESSDENIED = unchecked((int) 0xC00D4286);
+
+ ///
+ /// MessageId: MF_E_NET_CANNOTCONNECT
+ ///
+ /// MessageText:
+ ///
+ /// Unable to establish a connection to the server.%0
+ ///
+ public const int MF_E_NET_CANNOTCONNECT = unchecked((int) 0xC00D4287);
+
+ ///
+ /// MessageId: MF_E_NET_INVALID_PUSH_TEMPLATE
+ ///
+ /// MessageText:
+ ///
+ /// The specified push template is invalid.%0
+ ///
+ public const int MF_E_NET_INVALID_PUSH_TEMPLATE = unchecked((int) 0xC00D4288);
+
+ ///
+ /// MessageId: MF_E_NET_INVALID_PUSH_PUBLISHING_POINT
+ ///
+ /// MessageText:
+ ///
+ /// The specified push publishing point is invalid.%0
+ ///
+ public const int MF_E_NET_INVALID_PUSH_PUBLISHING_POINT = unchecked((int) 0xC00D4289);
+
+ ///
+ /// MessageId: MF_E_NET_BUSY
+ ///
+ /// MessageText:
+ ///
+ /// The requested resource is in use.%0
+ ///
+ public const int MF_E_NET_BUSY = unchecked((int) 0xC00D428A);
+
+ ///
+ /// MessageId: MF_E_NET_RESOURCE_GONE
+ ///
+ /// MessageText:
+ ///
+ /// The Publishing Point or file on the Windows Media Server is no longer available.%0
+ ///
+ public const int MF_E_NET_RESOURCE_GONE = unchecked((int) 0xC00D428B);
+
+ ///
+ /// MessageId: MF_E_NET_ERROR_FROM_PROXY
+ ///
+ /// MessageText:
+ ///
+ /// The proxy experienced an error while attempting to contact the media server.%0
+ ///
+ public const int MF_E_NET_ERROR_FROM_PROXY = unchecked((int) 0xC00D428C);
+
+ ///
+ /// MessageId: MF_E_NET_PROXY_TIMEOUT
+ ///
+ /// MessageText:
+ ///
+ /// The proxy did not receive a timely response while attempting to contact the media server.%0
+ ///
+ public const int MF_E_NET_PROXY_TIMEOUT = unchecked((int) 0xC00D428D);
+
+ ///
+ /// MessageId: MF_E_NET_SERVER_UNAVAILABLE
+ ///
+ /// MessageText:
+ ///
+ /// The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.%0
+ ///
+ public const int MF_E_NET_SERVER_UNAVAILABLE = unchecked((int) 0xC00D428E);
+
+ ///
+ /// MessageId: MF_E_NET_TOO_MUCH_DATA
+ ///
+ /// MessageText:
+ ///
+ /// The encoding process was unable to keep up with the amount of supplied data.%0
+ ///
+ public const int MF_E_NET_TOO_MUCH_DATA = unchecked((int) 0xC00D428F);
+
+ ///
+ /// MessageId: MF_E_NET_SESSION_INVALID
+ ///
+ /// MessageText:
+ ///
+ /// Session not found.%0
+ ///
+ public const int MF_E_NET_SESSION_INVALID = unchecked((int) 0xC00D4290);
+
+ ///
+ /// MessageId: MF_E_OFFLINE_MODE
+ ///
+ /// MessageText:
+ ///
+ /// The requested URL is not available in offline mode.%0
+ ///
+ public const int MF_E_OFFLINE_MODE = unchecked((int) 0xC00D4291);
+
+ ///
+ /// MessageId: MF_E_NET_UDP_BLOCKED
+ ///
+ /// MessageText:
+ ///
+ /// A device in the network is blocking UDP traffic.%0
+ ///
+ public const int MF_E_NET_UDP_BLOCKED = unchecked((int) 0xC00D4292);
+
+ ///
+ /// MessageId: MF_E_NET_UNSUPPORTED_CONFIGURATION
+ ///
+ /// MessageText:
+ ///
+ /// The specified configuration value is not supported.%0
+ ///
+ public const int MF_E_NET_UNSUPPORTED_CONFIGURATION = unchecked((int) 0xC00D4293);
+
+ ///
+ /// MessageId: MF_E_NET_PROTOCOL_DISABLED
+ ///
+ /// MessageText:
+ ///
+ /// The networking protocol is disabled.%0
+ ///
+ public const int MF_E_NET_PROTOCOL_DISABLED = unchecked((int) 0xC00D4294);
+
+ #endregion
+ #region MEDIAFOUNDATION WMContainer Error Events
+
+ ///
+ /// MessageId: MF_E_ALREADY_INITIALIZED
+ ///
+ /// MessageText:
+ ///
+ /// This object has already been initialized and cannot be re-initialized at this time.%0
+ ///
+ public const int MF_E_ALREADY_INITIALIZED = unchecked((int) 0xC00D4650);
+
+ ///
+ /// MessageId: MF_E_BANDWIDTH_OVERRUN
+ ///
+ /// MessageText:
+ ///
+ /// The amount of data passed in exceeds the given bitrate and buffer window.%0
+ ///
+ public const int MF_E_BANDWIDTH_OVERRUN = unchecked((int) 0xC00D4651);
+
+ ///
+ /// MessageId: MF_E_LATE_SAMPLE
+ ///
+ /// MessageText:
+ ///
+ /// The sample was passed in too late to be correctly processed.%0
+ ///
+ public const int MF_E_LATE_SAMPLE = unchecked((int) 0xC00D4652);
+
+ ///
+ /// MessageId: MF_E_FLUSH_NEEDED
+ ///
+ /// MessageText:
+ ///
+ /// The requested action cannot be carried out until the object is flushed and the queue is emptied.%0
+ ///
+ public const int MF_E_FLUSH_NEEDED = unchecked((int) 0xC00D4653);
+
+ ///
+ /// MessageId: MF_E_INVALID_PROFILE
+ ///
+ /// MessageText:
+ ///
+ /// The profile is invalid.%0
+ ///
+ public const int MF_E_INVALID_PROFILE = unchecked((int) 0xC00D4654);
+
+ ///
+ /// MessageId: MF_E_INDEX_NOT_COMMITTED
+ ///
+ /// MessageText:
+ ///
+ /// The index that is being generated needs to be committed before the requested action can be carried out.%0
+ ///
+ public const int MF_E_INDEX_NOT_COMMITTED = unchecked((int) 0xC00D4655);
+
+ ///
+ /// MessageId: MF_E_NO_INDEX
+ ///
+ /// MessageText:
+ ///
+ /// The index that is necessary for the requested action is not found.%0
+ ///
+ public const int MF_E_NO_INDEX = unchecked((int) 0xC00D4656);
+
+ ///
+ /// MessageId: MF_E_CANNOT_INDEX_IN_PLACE
+ ///
+ /// MessageText:
+ ///
+ /// The requested index cannot be added in-place to the specified ASF content.%0
+ ///
+ public const int MF_E_CANNOT_INDEX_IN_PLACE = unchecked((int) 0xC00D4657);
+
+ ///
+ /// MessageId: MF_E_MISSING_ASF_LEAKYBUCKET
+ ///
+ /// MessageText:
+ ///
+ /// The ASF leaky bucket parameters must be specified in order to carry out this request.%0
+ ///
+ public const int MF_E_MISSING_ASF_LEAKYBUCKET = unchecked((int) 0xC00D4658);
+
+ ///
+ /// MessageId: MF_E_INVALID_ASF_STREAMID
+ ///
+ /// MessageText:
+ ///
+ /// The stream id is invalid. The valid range for ASF stream id is from 1 to 127.%0
+ ///
+ public const int MF_E_INVALID_ASF_STREAMID = unchecked((int) 0xC00D4659);
+
+ #endregion
+ #region MEDIAFOUNDATION Media Sink Error Events
+
+ ///
+ /// MessageId: MF_E_STREAMSINK_REMOVED
+ ///
+ /// MessageText:
+ ///
+ /// The requested Stream Sink has been removed and cannot be used.%0
+ ///
+ public const int MF_E_STREAMSINK_REMOVED = unchecked((int) 0xC00D4A38);
+
+ ///
+ /// MessageId: MF_E_STREAMSINKS_OUT_OF_SYNC
+ ///
+ /// MessageText:
+ ///
+ /// The various Stream Sinks in this Media Sink are too far out of sync for the requested action to take place.%0
+ ///
+ public const int MF_E_STREAMSINKS_OUT_OF_SYNC = unchecked((int) 0xC00D4A3A);
+
+ ///
+ /// MessageId: MF_E_STREAMSINKS_FIXED
+ ///
+ /// MessageText:
+ ///
+ /// Stream Sinks cannot be added to or removed from this Media Sink because its set of streams is fixed.%0
+ ///
+ public const int MF_E_STREAMSINKS_FIXED = unchecked((int) 0xC00D4A3B);
+
+ ///
+ /// MessageId: MF_E_STREAMSINK_EXISTS
+ ///
+ /// MessageText:
+ ///
+ /// The given Stream Sink already exists.%0
+ ///
+ public const int MF_E_STREAMSINK_EXISTS = unchecked((int) 0xC00D4A3C);
+
+ ///
+ /// MessageId: MF_E_SAMPLEALLOCATOR_CANCELED
+ ///
+ /// MessageText:
+ ///
+ /// Sample allocations have been canceled.%0
+ ///
+ public const int MF_E_SAMPLEALLOCATOR_CANCELED = unchecked((int) 0xC00D4A3D);
+
+ ///
+ /// MessageId: MF_E_SAMPLEALLOCATOR_EMPTY
+ ///
+ /// MessageText:
+ ///
+ /// The sample allocator is currently empty, due to outstanding requests.%0
+ ///
+ public const int MF_E_SAMPLEALLOCATOR_EMPTY = unchecked((int) 0xC00D4A3E);
+
+ ///
+ /// MessageId: MF_E_SINK_ALREADYSTOPPED
+ ///
+ /// MessageText:
+ ///
+ /// When we try to sopt a stream sink, it is already stopped %0
+ ///
+ public const int MF_E_SINK_ALREADYSTOPPED = unchecked((int) 0xC00D4A3F);
+
+ ///
+ /// MessageId: MF_E_ASF_FILESINK_BITRATE_UNKNOWN
+ ///
+ /// MessageText:
+ ///
+ /// The ASF file sink could not reserve AVIO because the bitrate is unknown.%0
+ ///
+ public const int MF_E_ASF_FILESINK_BITRATE_UNKNOWN = unchecked((int) 0xC00D4A40);
+
+ ///
+ /// MessageId: MF_E_SINK_NO_STREAMS
+ ///
+ /// MessageText:
+ ///
+ /// No streams are selected in sink presentation descriptor.%0
+ ///
+ public const int MF_E_SINK_NO_STREAMS = unchecked((int) 0xC00D4A41);
+
+ ///
+ /// MessageId: MF_S_SINK_NOT_FINALIZED
+ ///
+ /// MessageText:
+ ///
+ /// The sink has not been finalized before shut down. This may cause sink generate a corrupted content.%0
+ ///
+ public const int MF_S_SINK_NOT_FINALIZED = unchecked((int) 0x000D4A42);
+
+ ///
+ /// MessageId: MF_E_METADATA_TOO_LONG
+ ///
+ /// MessageText:
+ ///
+ /// A metadata item was too long to write to the output container.%0
+ ///
+ public const int MF_E_METADATA_TOO_LONG = unchecked((int) 0xC00D4A43);
+
+ ///
+ /// MessageId: MF_E_SINK_NO_SAMPLES_PROCESSED
+ ///
+ /// MessageText:
+ ///
+ /// The operation failed because no samples were processed by the sink.%0
+ ///
+ public const int MF_E_SINK_NO_SAMPLES_PROCESSED = unchecked((int) 0xC00D4A44);
+
+ #endregion
+ #region MEDIAFOUNDATION Renderer Error Events
+
+ ///
+ /// MessageId: MF_E_VIDEO_REN_NO_PROCAMP_HW
+ ///
+ /// MessageText:
+ ///
+ /// There is no available procamp hardware with which to perform color correction.%0
+ ///
+ public const int MF_E_VIDEO_REN_NO_PROCAMP_HW = unchecked((int) 0xC00D4E20);
+
+ ///
+ /// MessageId: MF_E_VIDEO_REN_NO_DEINTERLACE_HW
+ ///
+ /// MessageText:
+ ///
+ /// There is no available deinterlacing hardware with which to deinterlace the video stream.%0
+ ///
+ public const int MF_E_VIDEO_REN_NO_DEINTERLACE_HW = unchecked((int) 0xC00D4E21);
+
+ ///
+ /// MessageId: MF_E_VIDEO_REN_COPYPROT_FAILED
+ ///
+ /// MessageText:
+ ///
+ /// A video stream requires copy protection to be enabled, but there was a failure in attempting to enable copy protection.%0
+ ///
+ public const int MF_E_VIDEO_REN_COPYPROT_FAILED = unchecked((int) 0xC00D4E22);
+
+ ///
+ /// MessageId: MF_E_VIDEO_REN_SURFACE_NOT_SHARED
+ ///
+ /// MessageText:
+ ///
+ /// A component is attempting to access a surface for sharing that is not shared.%0
+ ///
+ public const int MF_E_VIDEO_REN_SURFACE_NOT_SHARED = unchecked((int) 0xC00D4E23);
+
+ ///
+ /// MessageId: MF_E_VIDEO_DEVICE_LOCKED
+ ///
+ /// MessageText:
+ ///
+ /// A component is attempting to access a shared device that is already locked by another component.%0
+ ///
+ public const int MF_E_VIDEO_DEVICE_LOCKED = unchecked((int) 0xC00D4E24);
+
+ ///
+ /// MessageId: MF_E_NEW_VIDEO_DEVICE
+ ///
+ /// MessageText:
+ ///
+ /// The device is no longer available. The handle should be closed and a new one opened.%0
+ ///
+ public const int MF_E_NEW_VIDEO_DEVICE = unchecked((int) 0xC00D4E25);
+
+ ///
+ /// MessageId: MF_E_NO_VIDEO_SAMPLE_AVAILABLE
+ ///
+ /// MessageText:
+ ///
+ /// A video sample is not currently queued on a stream that is required for mixing.%0
+ ///
+ public const int MF_E_NO_VIDEO_SAMPLE_AVAILABLE = unchecked((int) 0xC00D4E26);
+
+ ///
+ /// MessageId: MF_E_NO_AUDIO_PLAYBACK_DEVICE
+ ///
+ /// MessageText:
+ ///
+ /// No audio playback device was found.%0
+ ///
+ public const int MF_E_NO_AUDIO_PLAYBACK_DEVICE = unchecked((int) 0xC00D4E84);
+
+ ///
+ /// MessageId: MF_E_AUDIO_PLAYBACK_DEVICE_IN_USE
+ ///
+ /// MessageText:
+ ///
+ /// The requested audio playback device is currently in use.%0
+ ///
+ public const int MF_E_AUDIO_PLAYBACK_DEVICE_IN_USE = unchecked((int) 0xC00D4E85);
+
+ ///
+ /// MessageId: MF_E_AUDIO_PLAYBACK_DEVICE_INVALIDATED
+ ///
+ /// MessageText:
+ ///
+ /// The audio playback device is no longer present.%0
+ ///
+ public const int MF_E_AUDIO_PLAYBACK_DEVICE_INVALIDATED = unchecked((int) 0xC00D4E86);
+
+ ///
+ /// MessageId: MF_E_AUDIO_SERVICE_NOT_RUNNING
+ ///
+ /// MessageText:
+ ///
+ /// The audio service is not running.%0
+ ///
+ public const int MF_E_AUDIO_SERVICE_NOT_RUNNING = unchecked((int) 0xC00D4E87);
+
+ #endregion
+ #region MEDIAFOUNDATION Topology Error Events
+
+ ///
+ /// MessageId: MF_E_TOPO_INVALID_OPTIONAL_NODE
+ ///
+ /// MessageText:
+ ///
+ /// The topology contains an invalid optional node. Possible reasons are incorrect number of outputs and inputs or optional node is at the beginning or end of a segment. %0
+ ///
+ public const int MF_E_TOPO_INVALID_OPTIONAL_NODE = unchecked((int) 0xC00D520E);
+
+ ///
+ /// MessageId: MF_E_TOPO_CANNOT_FIND_DECRYPTOR
+ ///
+ /// MessageText:
+ ///
+ /// No suitable transform was found to decrypt the content. %0
+ ///
+ public const int MF_E_TOPO_CANNOT_FIND_DECRYPTOR = unchecked((int) 0xC00D5211);
+
+ ///
+ /// MessageId: MF_E_TOPO_CODEC_NOT_FOUND
+ ///
+ /// MessageText:
+ ///
+ /// No suitable transform was found to encode or decode the content. %0
+ ///
+ public const int MF_E_TOPO_CODEC_NOT_FOUND = unchecked((int) 0xC00D5212);
+
+ ///
+ /// MessageId: MF_E_TOPO_CANNOT_CONNECT
+ ///
+ /// MessageText:
+ ///
+ /// Unable to find a way to connect nodes%0
+ ///
+ public const int MF_E_TOPO_CANNOT_CONNECT = unchecked((int) 0xC00D5213);
+
+ ///
+ /// MessageId: MF_E_TOPO_UNSUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// Unsupported operations in topoloader%0
+ ///
+ public const int MF_E_TOPO_UNSUPPORTED = unchecked((int) 0xC00D5214);
+
+ ///
+ /// MessageId: MF_E_TOPO_INVALID_TIME_ATTRIBUTES
+ ///
+ /// MessageText:
+ ///
+ /// The topology or its nodes contain incorrectly set time attributes%0
+ ///
+ public const int MF_E_TOPO_INVALID_TIME_ATTRIBUTES = unchecked((int) 0xC00D5215);
+
+ ///
+ /// MessageId: MF_E_TOPO_LOOPS_IN_TOPOLOGY
+ ///
+ /// MessageText:
+ ///
+ /// The topology contains loops, which are unsupported in media foundation topologies%0
+ ///
+ public const int MF_E_TOPO_LOOPS_IN_TOPOLOGY = unchecked((int) 0xC00D5216);
+
+ ///
+ /// MessageId: MF_E_TOPO_MISSING_PRESENTATION_DESCRIPTOR
+ ///
+ /// MessageText:
+ ///
+ /// A source stream node in the topology does not have a presentation descriptor%0
+ ///
+ public const int MF_E_TOPO_MISSING_PRESENTATION_DESCRIPTOR = unchecked((int) 0xC00D5217);
+
+ ///
+ /// MessageId: MF_E_TOPO_MISSING_STREAM_DESCRIPTOR
+ ///
+ /// MessageText:
+ ///
+ /// A source stream node in the topology does not have a stream descriptor%0
+ ///
+ public const int MF_E_TOPO_MISSING_STREAM_DESCRIPTOR = unchecked((int) 0xC00D5218);
+
+ ///
+ /// MessageId: MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED
+ ///
+ /// MessageText:
+ ///
+ /// A stream descriptor was set on a source stream node but it was not selected on the presentation descriptor%0
+ ///
+ public const int MF_E_TOPO_STREAM_DESCRIPTOR_NOT_SELECTED = unchecked((int) 0xC00D5219);
+
+ ///
+ /// MessageId: MF_E_TOPO_MISSING_SOURCE
+ ///
+ /// MessageText:
+ ///
+ /// A source stream node in the topology does not have a source%0
+ ///
+ public const int MF_E_TOPO_MISSING_SOURCE = unchecked((int) 0xC00D521A);
+
+ ///
+ /// MessageId: MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The topology loader does not support sink activates on output nodes.%0
+ ///
+ public const int MF_E_TOPO_SINK_ACTIVATES_UNSUPPORTED = unchecked((int) 0xC00D521B);
+ #endregion
+ #region MEDIAFOUNDATION Timeline Error Events
+
+ ///
+ /// MessageId: MF_E_SEQUENCER_UNKNOWN_SEGMENT_ID
+ ///
+ /// MessageText:
+ ///
+ /// The sequencer cannot find a segment with the given ID.%0\n.
+ ///
+ public const int MF_E_SEQUENCER_UNKNOWN_SEGMENT_ID = unchecked((int) 0xC00D61AC);
+
+ ///
+ /// MessageId: MF_S_SEQUENCER_CONTEXT_CANCELED
+ ///
+ /// MessageText:
+ ///
+ /// The context was canceled.%0\n.
+ ///
+ public const int MF_S_SEQUENCER_CONTEXT_CANCELED = unchecked((int) 0x000D61AD);
+
+ ///
+ /// MessageId: MF_E_NO_SOURCE_IN_CACHE
+ ///
+ /// MessageText:
+ ///
+ /// Cannot find source in source cache.%0\n.
+ ///
+ public const int MF_E_NO_SOURCE_IN_CACHE = unchecked((int) 0xC00D61AE);
+
+ ///
+ /// MessageId: MF_S_SEQUENCER_SEGMENT_AT_END_OF_STREAM
+ ///
+ /// MessageText:
+ ///
+ /// Cannot update topology flags.%0\n.
+ ///
+ public const int MF_S_SEQUENCER_SEGMENT_AT_END_OF_STREAM = unchecked((int) 0x000D61AF);
+ #endregion
+ #region Transform errors
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_TYPE_NOT_SET
+ ///
+ /// MessageText:
+ ///
+ /// A valid type has not been set for this stream or a stream that it depends on.%0
+ ///
+ public const int MF_E_TRANSFORM_TYPE_NOT_SET = unchecked((int) 0xC00D6D60);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_STREAM_CHANGE
+ ///
+ /// MessageText:
+ ///
+ /// A stream change has occurred. Output cannot be produced until the streams have been renegotiated.%0
+ ///
+ public const int MF_E_TRANSFORM_STREAM_CHANGE = unchecked((int) 0xC00D6D61);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_INPUT_REMAINING
+ ///
+ /// MessageText:
+ ///
+ /// The transform cannot take the requested action until all of the input data it currently holds is processed or flushed.%0
+ ///
+ public const int MF_E_TRANSFORM_INPUT_REMAINING = unchecked((int) 0xC00D6D62);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROFILE_MISSING
+ ///
+ /// MessageText:
+ ///
+ /// The transform requires a profile but no profile was supplied or found.%0
+ ///
+ public const int MF_E_TRANSFORM_PROFILE_MISSING = unchecked((int) 0xC00D6D63);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROFILE_INVALID_OR_CORRUPT
+ ///
+ /// MessageText:
+ ///
+ /// The transform requires a profile but the supplied profile was invalid or corrupt.%0
+ ///
+ public const int MF_E_TRANSFORM_PROFILE_INVALID_OR_CORRUPT = unchecked((int) 0xC00D6D64);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROFILE_TRUNCATED
+ ///
+ /// MessageText:
+ ///
+ /// The transform requires a profile but the supplied profile ended unexpectedly while parsing.%0
+ ///
+ public const int MF_E_TRANSFORM_PROFILE_TRUNCATED = unchecked((int) 0xC00D6D65);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROPERTY_PID_NOT_RECOGNIZED
+ ///
+ /// MessageText:
+ ///
+ /// The property ID does not match any property supported by the transform.%0
+ ///
+ public const int MF_E_TRANSFORM_PROPERTY_PID_NOT_RECOGNIZED = unchecked((int) 0xC00D6D66);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROPERTY_VARIANT_TYPE_WRONG
+ ///
+ /// MessageText:
+ ///
+ /// The variant does not have the type expected for this property ID.%0
+ ///
+ public const int MF_E_TRANSFORM_PROPERTY_VARIANT_TYPE_WRONG = unchecked((int) 0xC00D6D67);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROPERTY_NOT_WRITEABLE
+ ///
+ /// MessageText:
+ ///
+ /// An attempt was made to set the value on a read-only property.%0
+ ///
+ public const int MF_E_TRANSFORM_PROPERTY_NOT_WRITEABLE = unchecked((int) 0xC00D6D68);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROPERTY_ARRAY_VALUE_WRONG_NUM_DIM
+ ///
+ /// MessageText:
+ ///
+ /// The array property value has an unexpected number of dimensions.%0
+ ///
+ public const int MF_E_TRANSFORM_PROPERTY_ARRAY_VALUE_WRONG_NUM_DIM = unchecked((int) 0xC00D6D69);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROPERTY_VALUE_SIZE_WRONG
+ ///
+ /// MessageText:
+ ///
+ /// The array or blob property value has an unexpected size.%0
+ ///
+ public const int MF_E_TRANSFORM_PROPERTY_VALUE_SIZE_WRONG = unchecked((int) 0xC00D6D6A);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROPERTY_VALUE_OUT_OF_RANGE
+ ///
+ /// MessageText:
+ ///
+ /// The property value is out of range for this transform.%0
+ ///
+ public const int MF_E_TRANSFORM_PROPERTY_VALUE_OUT_OF_RANGE = unchecked((int) 0xC00D6D6B);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_PROPERTY_VALUE_INCOMPATIBLE
+ ///
+ /// MessageText:
+ ///
+ /// The property value is incompatible with some other property or mediatype set on the transform.%0
+ ///
+ public const int MF_E_TRANSFORM_PROPERTY_VALUE_INCOMPATIBLE = unchecked((int) 0xC00D6D6C);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_OUTPUT_MEDIATYPE
+ ///
+ /// MessageText:
+ ///
+ /// The requested operation is not supported for the currently set output mediatype.%0
+ ///
+ public const int MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_OUTPUT_MEDIATYPE = unchecked((int) 0xC00D6D6D);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_INPUT_MEDIATYPE
+ ///
+ /// MessageText:
+ ///
+ /// The requested operation is not supported for the currently set input mediatype.%0
+ ///
+ public const int MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_INPUT_MEDIATYPE = unchecked((int) 0xC00D6D6E);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_MEDIATYPE_COMBINATION
+ ///
+ /// MessageText:
+ ///
+ /// The requested operation is not supported for the currently set combination of mediatypes.%0
+ ///
+ public const int MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_MEDIATYPE_COMBINATION = unchecked((int) 0xC00D6D6F);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_CONFLICTS_WITH_OTHER_CURRENTLY_ENABLED_FEATURES
+ ///
+ /// MessageText:
+ ///
+ /// The requested feature is not supported in combination with some other currently enabled feature.%0
+ ///
+ public const int MF_E_TRANSFORM_CONFLICTS_WITH_OTHER_CURRENTLY_ENABLED_FEATURES = unchecked((int) 0xC00D6D70);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_NEED_MORE_INPUT
+ ///
+ /// MessageText:
+ ///
+ /// The transform cannot produce output until it gets more input samples.%0
+ ///
+ public const int MF_E_TRANSFORM_NEED_MORE_INPUT = unchecked((int) 0xC00D6D72);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_SPKR_CONFIG
+ ///
+ /// MessageText:
+ ///
+ /// The requested operation is not supported for the current speaker configuration.%0
+ ///
+ public const int MF_E_TRANSFORM_NOT_POSSIBLE_FOR_CURRENT_SPKR_CONFIG = unchecked((int) 0xC00D6D73);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING
+ ///
+ /// MessageText:
+ ///
+ /// The transform cannot accept mediatype changes in the middle of processing.%0
+ ///
+ public const int MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING = unchecked((int) 0xC00D6D74);
+
+ ///
+ /// MessageId: MF_S_TRANSFORM_DO_NOT_PROPAGATE_EVENT
+ ///
+ /// MessageText:
+ ///
+ /// The caller should not propagate this event to downstream components.%0
+ ///
+ public const int MF_S_TRANSFORM_DO_NOT_PROPAGATE_EVENT = unchecked((int) 0x000D6D75);
+
+ ///
+ /// MessageId: MF_E_UNSUPPORTED_D3D_TYPE
+ ///
+ /// MessageText:
+ ///
+ /// The input type is not supported for D3D device.%0
+ ///
+ public const int MF_E_UNSUPPORTED_D3D_TYPE = unchecked((int) 0xC00D6D76);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_ASYNC_LOCKED
+ ///
+ /// MessageText:
+ ///
+ /// The caller does not appear to support this transform's asynchronous capabilities.%0
+ ///
+ public const int MF_E_TRANSFORM_ASYNC_LOCKED = unchecked((int) 0xC00D6D77);
+
+ ///
+ /// MessageId: MF_E_TRANSFORM_CANNOT_INITIALIZE_ACM_DRIVER
+ ///
+ /// MessageText:
+ ///
+ /// An audio compression manager driver could not be initialized by the transform.%0
+ ///
+ public const int MF_E_TRANSFORM_CANNOT_INITIALIZE_ACM_DRIVER = unchecked((int) 0xC00D6D78);
+
+ #endregion
+ #region Content Protection errors
+
+ ///
+ /// MessageId: MF_E_LICENSE_INCORRECT_RIGHTS
+ ///
+ /// MessageText:
+ ///
+ /// You are not allowed to open this file. Contact the content provider for further assistance.%0
+ ///
+ public const int MF_E_LICENSE_INCORRECT_RIGHTS = unchecked((int) 0xC00D7148);
+
+ ///
+ /// MessageId: MF_E_LICENSE_OUTOFDATE
+ ///
+ /// MessageText:
+ ///
+ /// The license for this media file has expired. Get a new license or contact the content provider for further assistance.%0
+ ///
+ public const int MF_E_LICENSE_OUTOFDATE = unchecked((int) 0xC00D7149);
+
+ ///
+ /// MessageId: MF_E_LICENSE_REQUIRED
+ ///
+ /// MessageText:
+ ///
+ /// You need a license to perform the requested operation on this media file.%0
+ ///
+ public const int MF_E_LICENSE_REQUIRED = unchecked((int) 0xC00D714A);
+
+ ///
+ /// MessageId: MF_E_DRM_HARDWARE_INCONSISTENT
+ ///
+ /// MessageText:
+ ///
+ /// The licenses for your media files are corrupted. Contact Microsoft product support.%0
+ ///
+ public const int MF_E_DRM_HARDWARE_INCONSISTENT = unchecked((int) 0xC00D714B);
+
+ ///
+ /// MessageId: MF_E_NO_CONTENT_PROTECTION_MANAGER
+ ///
+ /// MessageText:
+ ///
+ /// The APP needs to provide IMFContentProtectionManager callback to access the protected media file.%0
+ ///
+ public const int MF_E_NO_CONTENT_PROTECTION_MANAGER = unchecked((int) 0xC00D714C);
+
+ ///
+ /// MessageId: MF_E_LICENSE_RESTORE_NO_RIGHTS
+ ///
+ /// MessageText:
+ ///
+ /// Client does not have rights to restore licenses.%0
+ ///
+ public const int MF_E_LICENSE_RESTORE_NO_RIGHTS = unchecked((int) 0xC00D714D);
+
+ ///
+ /// MessageId: MF_E_BACKUP_RESTRICTED_LICENSE
+ ///
+ /// MessageText:
+ ///
+ /// Licenses are restricted and hence can not be backed up.%0
+ ///
+ public const int MF_E_BACKUP_RESTRICTED_LICENSE = unchecked((int) 0xC00D714E);
+
+ ///
+ /// MessageId: MF_E_LICENSE_RESTORE_NEEDS_INDIVIDUALIZATION
+ ///
+ /// MessageText:
+ ///
+ /// License restore requires machine to be individualized.%0
+ ///
+ public const int MF_E_LICENSE_RESTORE_NEEDS_INDIVIDUALIZATION = unchecked((int) 0xC00D714F);
+
+ ///
+ /// MessageId: MF_S_PROTECTION_NOT_REQUIRED
+ ///
+ /// MessageText:
+ ///
+ /// Protection for stream is not required.%0
+ ///
+ public const int MF_S_PROTECTION_NOT_REQUIRED = unchecked((int) 0x000D7150);
+
+ ///
+ /// MessageId: MF_E_COMPONENT_REVOKED
+ ///
+ /// MessageText:
+ ///
+ /// Component is revoked.%0
+ ///
+ public const int MF_E_COMPONENT_REVOKED = unchecked((int) 0xC00D7151);
+
+ ///
+ /// MessageId: MF_E_TRUST_DISABLED
+ ///
+ /// MessageText:
+ ///
+ /// Trusted functionality is currently disabled on this component.%0
+ ///
+ public const int MF_E_TRUST_DISABLED = unchecked((int) 0xC00D7152);
+
+ ///
+ /// MessageId: MF_E_WMDRMOTA_NO_ACTION
+ ///
+ /// MessageText:
+ ///
+ /// No Action is set on WMDRM Output Trust Authority.%0
+ ///
+ public const int MF_E_WMDRMOTA_NO_ACTION = unchecked((int) 0xC00D7153);
+
+ ///
+ /// MessageId: MF_E_WMDRMOTA_ACTION_ALREADY_SET
+ ///
+ /// MessageText:
+ ///
+ /// Action is already set on WMDRM Output Trust Authority.%0
+ ///
+ public const int MF_E_WMDRMOTA_ACTION_ALREADY_SET = unchecked((int) 0xC00D7154);
+
+ ///
+ /// MessageId: MF_E_WMDRMOTA_DRM_HEADER_NOT_AVAILABLE
+ ///
+ /// MessageText:
+ ///
+ /// DRM Heaader is not available.%0
+ ///
+ public const int MF_E_WMDRMOTA_DRM_HEADER_NOT_AVAILABLE = unchecked((int) 0xC00D7155);
+
+ ///
+ /// MessageId: MF_E_WMDRMOTA_DRM_ENCRYPTION_SCHEME_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// Current encryption scheme is not supported.%0
+ ///
+ public const int MF_E_WMDRMOTA_DRM_ENCRYPTION_SCHEME_NOT_SUPPORTED = unchecked((int) 0xC00D7156);
+
+ ///
+ /// MessageId: MF_E_WMDRMOTA_ACTION_MISMATCH
+ ///
+ /// MessageText:
+ ///
+ /// Action does not match with current configuration.%0
+ ///
+ public const int MF_E_WMDRMOTA_ACTION_MISMATCH = unchecked((int) 0xC00D7157);
+
+ ///
+ /// MessageId: MF_E_WMDRMOTA_INVALID_POLICY
+ ///
+ /// MessageText:
+ ///
+ /// Invalid policy for WMDRM Output Trust Authority.%0
+ ///
+ public const int MF_E_WMDRMOTA_INVALID_POLICY = unchecked((int) 0xC00D7158);
+
+ ///
+ /// MessageId: MF_E_POLICY_UNSUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The policies that the Input Trust Authority requires to be enforced are unsupported by the outputs.%0
+ ///
+ public const int MF_E_POLICY_UNSUPPORTED = unchecked((int) 0xC00D7159);
+
+ ///
+ /// MessageId: MF_E_OPL_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The OPL that the license requires to be enforced are not supported by the Input Trust Authority.%0
+ ///
+ public const int MF_E_OPL_NOT_SUPPORTED = unchecked((int) 0xC00D715A);
+
+ ///
+ /// MessageId: MF_E_TOPOLOGY_VERIFICATION_FAILED
+ ///
+ /// MessageText:
+ ///
+ /// The topology could not be successfully verified.%0
+ ///
+ public const int MF_E_TOPOLOGY_VERIFICATION_FAILED = unchecked((int) 0xC00D715B);
+
+ ///
+ /// MessageId: MF_E_SIGNATURE_VERIFICATION_FAILED
+ ///
+ /// MessageText:
+ ///
+ /// Signature verification could not be completed successfully for this component.%0
+ ///
+ public const int MF_E_SIGNATURE_VERIFICATION_FAILED = unchecked((int) 0xC00D715C);
+
+ ///
+ /// MessageId: MF_E_DEBUGGING_NOT_ALLOWED
+ ///
+ /// MessageText:
+ ///
+ /// Running this process under a debugger while using protected content is not allowed.%0
+ ///
+ public const int MF_E_DEBUGGING_NOT_ALLOWED = unchecked((int) 0xC00D715D);
+
+ ///
+ /// MessageId: MF_E_CODE_EXPIRED
+ ///
+ /// MessageText:
+ ///
+ /// MF component has expired.%0
+ ///
+ public const int MF_E_CODE_EXPIRED = unchecked((int) 0xC00D715E);
+
+ ///
+ /// MessageId: MF_E_GRL_VERSION_TOO_LOW
+ ///
+ /// MessageText:
+ ///
+ /// The current GRL on the machine does not meet the minimum version requirements.%0
+ ///
+ public const int MF_E_GRL_VERSION_TOO_LOW = unchecked((int) 0xC00D715F);
+
+ ///
+ /// MessageId: MF_E_GRL_RENEWAL_NOT_FOUND
+ ///
+ /// MessageText:
+ ///
+ /// The current GRL on the machine does not contain any renewal entries for the specified revocation.%0
+ ///
+ public const int MF_E_GRL_RENEWAL_NOT_FOUND = unchecked((int) 0xC00D7160);
+
+ ///
+ /// MessageId: MF_E_GRL_EXTENSIBLE_ENTRY_NOT_FOUND
+ ///
+ /// MessageText:
+ ///
+ /// The current GRL on the machine does not contain any extensible entries for the specified extension GUID.%0
+ ///
+ public const int MF_E_GRL_EXTENSIBLE_ENTRY_NOT_FOUND = unchecked((int) 0xC00D7161);
+
+ ///
+ /// MessageId: MF_E_KERNEL_UNTRUSTED
+ ///
+ /// MessageText:
+ ///
+ /// The kernel isn't secure for high security level content.%0
+ ///
+ public const int MF_E_KERNEL_UNTRUSTED = unchecked((int) 0xC00D7162);
+
+ ///
+ /// MessageId: MF_E_PEAUTH_UNTRUSTED
+ ///
+ /// MessageText:
+ ///
+ /// The response from protected environment driver isn't valid.%0
+ ///
+ public const int MF_E_PEAUTH_UNTRUSTED = unchecked((int) 0xC00D7163);
+
+ ///
+ /// MessageId: MF_E_NON_PE_PROCESS
+ ///
+ /// MessageText:
+ ///
+ /// A non-PE process tried to talk to PEAuth.%0
+ ///
+ public const int MF_E_NON_PE_PROCESS = unchecked((int) 0xC00D7165);
+
+ ///
+ /// MessageId: MF_E_REBOOT_REQUIRED
+ ///
+ /// MessageText:
+ ///
+ /// We need to reboot the machine.%0
+ ///
+ public const int MF_E_REBOOT_REQUIRED = unchecked((int) 0xC00D7167);
+
+ ///
+ /// MessageId: MF_S_WAIT_FOR_POLICY_SET
+ ///
+ /// MessageText:
+ ///
+ /// Protection for this stream is not guaranteed to be enforced until the MEPolicySet event is fired.%0
+ ///
+ public const int MF_S_WAIT_FOR_POLICY_SET = unchecked((int) 0x000D7168);
+
+ ///
+ /// MessageId: MF_S_VIDEO_DISABLED_WITH_UNKNOWN_SOFTWARE_OUTPUT
+ ///
+ /// MessageText:
+ ///
+ /// This video stream is disabled because it is being sent to an unknown software output.%0
+ ///
+ public const int MF_S_VIDEO_DISABLED_WITH_UNKNOWN_SOFTWARE_OUTPUT = unchecked((int) 0x000D7169);
+
+ ///
+ /// MessageId: MF_E_GRL_INVALID_FORMAT
+ ///
+ /// MessageText:
+ ///
+ /// The GRL file is not correctly formed, it may have been corrupted or overwritten.%0
+ ///
+ public const int MF_E_GRL_INVALID_FORMAT = unchecked((int) 0xC00D716A);
+
+ ///
+ /// MessageId: MF_E_GRL_UNRECOGNIZED_FORMAT
+ ///
+ /// MessageText:
+ ///
+ /// The GRL file is in a format newer than those recognized by this GRL Reader.%0
+ ///
+ public const int MF_E_GRL_UNRECOGNIZED_FORMAT = unchecked((int) 0xC00D716B);
+
+ ///
+ /// MessageId: MF_E_ALL_PROCESS_RESTART_REQUIRED
+ ///
+ /// MessageText:
+ ///
+ /// The GRL was reloaded and required all processes that can run protected media to restart.%0
+ ///
+ public const int MF_E_ALL_PROCESS_RESTART_REQUIRED = unchecked((int) 0xC00D716C);
+
+ ///
+ /// MessageId: MF_E_PROCESS_RESTART_REQUIRED
+ ///
+ /// MessageText:
+ ///
+ /// The GRL was reloaded and the current process needs to restart.%0
+ ///
+ public const int MF_E_PROCESS_RESTART_REQUIRED = unchecked((int) 0xC00D716D);
+
+ ///
+ /// MessageId: MF_E_USERMODE_UNTRUSTED
+ ///
+ /// MessageText:
+ ///
+ /// The user space is untrusted for protected content play.%0
+ ///
+ public const int MF_E_USERMODE_UNTRUSTED = unchecked((int) 0xC00D716E);
+
+ ///
+ /// MessageId: MF_E_PEAUTH_SESSION_NOT_STARTED
+ ///
+ /// MessageText:
+ ///
+ /// PEAuth communication session hasn't been started.%0
+ ///
+ public const int MF_E_PEAUTH_SESSION_NOT_STARTED = unchecked((int) 0xC00D716F);
+
+ ///
+ /// MessageId: MF_E_PEAUTH_PUBLICKEY_REVOKED
+ ///
+ /// MessageText:
+ ///
+ /// PEAuth's public key is revoked.%0
+ ///
+ public const int MF_E_PEAUTH_PUBLICKEY_REVOKED = unchecked((int) 0xC00D7171);
+
+ ///
+ /// MessageId: MF_E_GRL_ABSENT
+ ///
+ /// MessageText:
+ ///
+ /// The GRL is absent.%0
+ ///
+ public const int MF_E_GRL_ABSENT = unchecked((int) 0xC00D7172);
+
+ ///
+ /// MessageId: MF_S_PE_TRUSTED
+ ///
+ /// MessageText:
+ ///
+ /// The Protected Environment is trusted.%0
+ ///
+ public const int MF_S_PE_TRUSTED = unchecked((int) 0x000D7173);
+
+ ///
+ /// MessageId: MF_E_PE_UNTRUSTED
+ ///
+ /// MessageText:
+ ///
+ /// The Protected Environment is untrusted.%0
+ ///
+ public const int MF_E_PE_UNTRUSTED = unchecked((int) 0xC00D7174);
+
+ ///
+ /// MessageId: MF_E_PEAUTH_NOT_STARTED
+ ///
+ /// MessageText:
+ ///
+ /// The Protected Environment Authorization service (PEAUTH) has not been started.%0
+ ///
+ public const int MF_E_PEAUTH_NOT_STARTED = unchecked((int) 0xC00D7175);
+
+ ///
+ /// MessageId: MF_E_INCOMPATIBLE_SAMPLE_PROTECTION
+ ///
+ /// MessageText:
+ ///
+ /// The sample protection algorithms supported by components are not compatible.%0
+ ///
+ public const int MF_E_INCOMPATIBLE_SAMPLE_PROTECTION = unchecked((int) 0xC00D7176);
+
+ ///
+ /// MessageId: MF_E_PE_SESSIONS_MAXED
+ ///
+ /// MessageText:
+ ///
+ /// No more protected environment sessions can be supported.%0
+ ///
+ public const int MF_E_PE_SESSIONS_MAXED = unchecked((int) 0xC00D7177);
+
+ ///
+ /// MessageId: MF_E_HIGH_SECURITY_LEVEL_CONTENT_NOT_ALLOWED
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA does not allow protected content with high security level for this release.%0
+ ///
+ public const int MF_E_HIGH_SECURITY_LEVEL_CONTENT_NOT_ALLOWED = unchecked((int) 0xC00D7178);
+
+ ///
+ /// MessageId: MF_E_TEST_SIGNED_COMPONENTS_NOT_ALLOWED
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA cannot allow the requested action for the content as one or more components is not properly signed.%0
+ ///
+ public const int MF_E_TEST_SIGNED_COMPONENTS_NOT_ALLOWED = unchecked((int) 0xC00D7179);
+
+ ///
+ /// MessageId: MF_E_ITA_UNSUPPORTED_ACTION
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA does not support the requested action.%0
+ ///
+ public const int MF_E_ITA_UNSUPPORTED_ACTION = unchecked((int) 0xC00D717A);
+
+ ///
+ /// MessageId: MF_E_ITA_ERROR_PARSING_SAP_PARAMETERS
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA encountered an error in parsing the Secure Audio Path parameters.%0
+ ///
+ public const int MF_E_ITA_ERROR_PARSING_SAP_PARAMETERS = unchecked((int) 0xC00D717B);
+
+ ///
+ /// MessageId: MF_E_POLICY_MGR_ACTION_OUTOFBOUNDS
+ ///
+ /// MessageText:
+ ///
+ /// The Policy Manager action passed in is invalid.%0
+ ///
+ public const int MF_E_POLICY_MGR_ACTION_OUTOFBOUNDS = unchecked((int) 0xC00D717C);
+
+ ///
+ /// MessageId: MF_E_BAD_OPL_STRUCTURE_FORMAT
+ ///
+ /// MessageText:
+ ///
+ /// The structure specifying Output Protection Level is not the correct format.%0
+ ///
+ public const int MF_E_BAD_OPL_STRUCTURE_FORMAT = unchecked((int) 0xC00D717D);
+
+ ///
+ /// MessageId: MF_E_ITA_UNRECOGNIZED_ANALOG_VIDEO_PROTECTION_GUID
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA does not recognize the Explicite Analog Video Output Protection guid specified in the license.%0
+ ///
+ public const int MF_E_ITA_UNRECOGNIZED_ANALOG_VIDEO_PROTECTION_GUID = unchecked((int) 0xC00D717E);
+
+ ///
+ /// MessageId: MF_E_NO_PMP_HOST
+ ///
+ /// MessageText:
+ ///
+ /// IMFPMPHost object not available.%0
+ ///
+ public const int MF_E_NO_PMP_HOST = unchecked((int) 0xC00D717F);
+
+ ///
+ /// MessageId: MF_E_ITA_OPL_DATA_NOT_INITIALIZED
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA could not initialize the Output Protection Level data.%0
+ ///
+ public const int MF_E_ITA_OPL_DATA_NOT_INITIALIZED = unchecked((int) 0xC00D7180);
+
+ ///
+ /// MessageId: MF_E_ITA_UNRECOGNIZED_ANALOG_VIDEO_OUTPUT
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA does not recognize the Analog Video Output specified by the OTA.%0
+ ///
+ public const int MF_E_ITA_UNRECOGNIZED_ANALOG_VIDEO_OUTPUT = unchecked((int) 0xC00D7181);
+
+ ///
+ /// MessageId: MF_E_ITA_UNRECOGNIZED_DIGITAL_VIDEO_OUTPUT
+ ///
+ /// MessageText:
+ ///
+ /// WMDRM ITA does not recognize the Digital Video Output specified by the OTA.%0
+ ///
+ public const int MF_E_ITA_UNRECOGNIZED_DIGITAL_VIDEO_OUTPUT = unchecked((int) 0xC00D7182);
+
+ #endregion
+ #region Clock errors
+
+ ///
+ /// MessageId: MF_E_CLOCK_INVALID_CONTINUITY_KEY
+ ///
+ /// MessageText:
+ ///
+ /// The continuity key supplied is not currently valid.%0
+ ///
+ public const int MF_E_CLOCK_INVALID_CONTINUITY_KEY = unchecked((int) 0xC00D9C40);
+
+ ///
+ /// MessageId: MF_E_CLOCK_NO_TIME_SOURCE
+ ///
+ /// MessageText:
+ ///
+ /// No Presentation Time Source has been specified.%0
+ ///
+ public const int MF_E_CLOCK_NO_TIME_SOURCE = unchecked((int) 0xC00D9C41);
+
+ ///
+ /// MessageId: MF_E_CLOCK_STATE_ALREADY_SET
+ ///
+ /// MessageText:
+ ///
+ /// The clock is already in the requested state.%0
+ ///
+ public const int MF_E_CLOCK_STATE_ALREADY_SET = unchecked((int) 0xC00D9C42);
+
+ ///
+ /// MessageId: MF_E_CLOCK_NOT_SIMPLE
+ ///
+ /// MessageText:
+ ///
+ /// The clock has too many advanced features to carry out the request.%0
+ ///
+ public const int MF_E_CLOCK_NOT_SIMPLE = unchecked((int) 0xC00D9C43);
+
+ ///
+ /// MessageId: MF_S_CLOCK_STOPPED
+ ///
+ /// MessageText:
+ ///
+ /// Timer::SetTimer returns this success code if called happened while timer is stopped. Timer is not going to be dispatched until clock is running%0
+ ///
+ public const int MF_S_CLOCK_STOPPED = unchecked((int) 0x000D9C44);
+ #endregion
+ #region MF Quality Management errors
+
+ ///
+ /// MessageId: MF_E_NO_MORE_DROP_MODES
+ ///
+ /// MessageText:
+ ///
+ /// The component does not support any more drop modes.%0
+ ///
+ public const int MF_E_NO_MORE_DROP_MODES = unchecked((int) 0xC00DA028);
+
+ ///
+ /// MessageId: MF_E_NO_MORE_QUALITY_LEVELS
+ ///
+ /// MessageText:
+ ///
+ /// The component does not support any more quality levels.%0
+ ///
+ public const int MF_E_NO_MORE_QUALITY_LEVELS = unchecked((int) 0xC00DA029);
+
+ ///
+ /// MessageId: MF_E_DROPTIME_NOT_SUPPORTED
+ ///
+ /// MessageText:
+ ///
+ /// The component does not support drop time functionality.%0
+ ///
+ public const int MF_E_DROPTIME_NOT_SUPPORTED = unchecked((int) 0xC00DA02A);
+
+ ///
+ /// MessageId: MF_E_QUALITYKNOB_WAIT_LONGER
+ ///
+ /// MessageText:
+ ///
+ /// Quality Manager needs to wait longer before bumping the Quality Level up.%0
+ ///
+ public const int MF_E_QUALITYKNOB_WAIT_LONGER = unchecked((int) 0xC00DA02B);
+
+ ///
+ /// MessageId: MF_E_QM_INVALIDSTATE
+ ///
+ /// MessageText:
+ ///
+ /// Quality Manager is in an invalid state. Quality Management is off at this moment.%0
+ ///
+ public const int MF_E_QM_INVALIDSTATE = unchecked((int) 0xC00DA02C);
+
+ #endregion
+ #region MF Transcode errors
+
+ ///
+ /// MessageId: MF_E_TRANSCODE_NO_CONTAINERTYPE
+ ///
+ /// MessageText:
+ ///
+ /// No transcode output container type is specified.%0
+ ///
+ public const int MF_E_TRANSCODE_NO_CONTAINERTYPE = unchecked((int) 0xC00DA410);
+
+ ///
+ /// MessageId: MF_E_TRANSCODE_PROFILE_NO_MATCHING_STREAMS
+ ///
+ /// MessageText:
+ ///
+ /// The profile does not have a media type configuration for any selected source streams.%0
+ ///
+ public const int MF_E_TRANSCODE_PROFILE_NO_MATCHING_STREAMS = unchecked((int) 0xC00DA411);
+
+ ///
+ /// MessageId: MF_E_TRANSCODE_NO_MATCHING_ENCODER
+ ///
+ /// MessageText:
+ ///
+ /// Cannot find an encoder MFT that accepts the user preferred output type.%0
+ ///
+ public const int MF_E_TRANSCODE_NO_MATCHING_ENCODER = unchecked((int) 0xC00DA412);
+
+ #endregion
+ #region MF HW Device Proxy errors
+
+ ///
+ /// MessageId: MF_E_ALLOCATOR_NOT_INITIALIZED
+ ///
+ /// MessageText:
+ ///
+ /// Memory allocator is not initialized.%0
+ ///
+ public const int MF_E_ALLOCATOR_NOT_INITIALIZED = unchecked((int) 0xC00DA7F8);
+
+ ///
+ /// MessageId: MF_E_ALLOCATOR_NOT_COMMITED
+ ///
+ /// MessageText:
+ ///
+ /// Memory allocator is not committed yet.%0
+ ///
+ public const int MF_E_ALLOCATOR_NOT_COMMITED = unchecked((int) 0xC00DA7F9);
+
+ ///
+ /// MessageId: MF_E_ALLOCATOR_ALREADY_COMMITED
+ ///
+ /// MessageText:
+ ///
+ /// Memory allocator has already been committed.%0
+ ///
+ public const int MF_E_ALLOCATOR_ALREADY_COMMITED = unchecked((int) 0xC00DA7FA);
+
+ ///
+ /// MessageId: MF_E_STREAM_ERROR
+ ///
+ /// MessageText:
+ ///
+ /// An error occurred in media stream.%0
+ ///
+ public const int MF_E_STREAM_ERROR = unchecked((int) 0xC00DA7FB);
+
+ ///
+ /// MessageId: MF_E_INVALID_STREAM_STATE
+ ///
+ /// MessageText:
+ ///
+ /// Stream is not in a state to handle the request.%0
+ ///
+ public const int MF_E_INVALID_STREAM_STATE = unchecked((int) 0xC00DA7FC);
+
+ ///
+ /// MessageId: MF_E_HW_STREAM_NOT_CONNECTED
+ ///
+ /// MessageText:
+ ///
+ /// Hardware stream is not connected yet.%0
+ ///
+ public const int MF_E_HW_STREAM_NOT_CONNECTED = unchecked((int) 0xC00DA7FD);
+
+ #endregion
+ }
+}
diff --git a/NAudio/MediaFoundation/MediaFoundationHelpers.cs b/NAudio/MediaFoundation/MediaFoundationHelpers.cs
new file mode 100644
index 00000000..3adf2490
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaFoundationHelpers.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Main interface for using Media Foundation with NAudio
+ ///
+ public static class MediaFoundationApi
+ {
+ private static bool initialized;
+
+ ///
+ /// initializes MediaFoundation - only needs to be called once per process
+ ///
+ public static void Startup()
+ {
+ if (!initialized)
+ {
+ MediaFoundationInterop.MFStartup(MediaFoundationInterop.MF_VERSION, 0);
+ initialized = true;
+ }
+ }
+
+#if !NETFX_CORE
+ ///
+ /// Enumerate the installed MediaFoundation transforms in the specified category
+ ///
+ /// A category from MediaFoundationTransformCategories
+ ///
+ public static IEnumerable EnumerateTransforms(Guid category)
+ {
+ IntPtr interfacesPointer;
+ int interfaceCount;
+ MediaFoundationInterop.MFTEnumEx(category, _MFT_ENUM_FLAG.MFT_ENUM_FLAG_ALL,
+ null, null, out interfacesPointer, out interfaceCount);
+ var interfaces = new IMFActivate[interfaceCount];
+ for (int n = 0; n < interfaceCount; n++)
+ {
+ var ptr =
+ Marshal.ReadIntPtr(new IntPtr(interfacesPointer.ToInt64() + n*Marshal.SizeOf(interfacesPointer)));
+ interfaces[n] = (IMFActivate) Marshal.GetObjectForIUnknown(ptr);
+ }
+
+ foreach (var i in interfaces)
+ {
+ yield return i;
+ }
+ Marshal.FreeCoTaskMem(interfacesPointer);
+ }
+#endif
+
+ ///
+ /// uninitializes MediaFoundation
+ ///
+ public static void Shutdown()
+ {
+ if (initialized)
+ {
+ MediaFoundationInterop.MFShutdown();
+ initialized = false;
+ }
+ }
+
+ ///
+ /// Creates a Media type
+ ///
+ public static IMFMediaType CreateMediaType()
+ {
+ IMFMediaType mediaType;
+ MediaFoundationInterop.MFCreateMediaType(out mediaType);
+ return mediaType;
+ }
+
+ ///
+ /// Creates a media type from a WaveFormat
+ ///
+ public static IMFMediaType CreateMediaTypeFromWaveFormat(WaveFormat waveFormat)
+ {
+ var mediaType = CreateMediaType();
+ try
+ {
+ MediaFoundationInterop.MFInitMediaTypeFromWaveFormatEx(mediaType, waveFormat, Marshal.SizeOf(waveFormat));
+ }
+ catch (Exception)
+ {
+ Marshal.ReleaseComObject(mediaType);
+ throw;
+ }
+ return mediaType;
+ }
+
+ ///
+ /// Creates a memory buffer of the specified size
+ ///
+ /// Memory buffer size in bytes
+ /// The memory buffer
+ public static IMFMediaBuffer CreateMemoryBuffer(int bufferSize)
+ {
+ IMFMediaBuffer buffer;
+ MediaFoundationInterop.MFCreateMemoryBuffer(bufferSize, out buffer);
+ return buffer;
+ }
+
+ ///
+ /// Creates a sample object
+ ///
+ /// The sample object
+ public static IMFSample CreateSample()
+ {
+ IMFSample sample;
+ MediaFoundationInterop.MFCreateSample(out sample);
+ return sample;
+ }
+
+ ///
+ /// Creates a new attributes store
+ ///
+ /// Initial size
+ /// The attributes store
+ public static IMFAttributes CreateAttributes(int initialSize)
+ {
+ IMFAttributes attributes;
+ MediaFoundationInterop.MFCreateAttributes(out attributes, initialSize);
+ return attributes;
+ }
+
+ ///
+ /// Creates a media foundation byte stream based on a stream object
+ /// (usable with WinRT streams)
+ ///
+ /// The input stream
+ /// A media foundation byte stream
+ public static IMFByteStream CreateByteStream(object stream)
+ {
+ IMFByteStream byteStream;
+ MediaFoundationInterop.MFCreateMFByteStreamOnStreamEx(stream, out byteStream);
+ return byteStream;
+ }
+
+ ///
+ /// Creates a source reader based on a byte stream
+ ///
+ /// The byte stream
+ /// A media foundation source reader
+ public static IMFSourceReader CreateSourceReaderFromByteStream(IMFByteStream byteStream)
+ {
+ IMFSourceReader reader;
+ MediaFoundationInterop.MFCreateSourceReaderFromByteStream(byteStream, null, out reader);
+ return reader;
+ }
+ }
+}
diff --git a/NAudio/MediaFoundation/MediaFoundationInterop.cs b/NAudio/MediaFoundation/MediaFoundationInterop.cs
new file mode 100644
index 00000000..407f6524
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaFoundationInterop.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Interop definitions for MediaFoundation
+ /// thanks to Lucian Wischik for the initial work on many of these definitions (also various interfaces)
+ /// n.b. the goal is to make as much of this internal as possible, and provide
+ /// better .NET APIs using the MediaFoundationApi class instead
+ ///
+ public static class MediaFoundationInterop
+ {
+ ///
+ /// Initializes Microsoft Media Foundation.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFStartup(int version, int dwFlags = 0);
+
+ ///
+ /// Shuts down the Microsoft Media Foundation platform
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFShutdown();
+
+ ///
+ /// Creates an empty media type.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ internal static extern void MFCreateMediaType(out IMFMediaType ppMFType);
+
+ ///
+ /// Initializes a media type from a WAVEFORMATEX structure.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ internal static extern void MFInitMediaTypeFromWaveFormatEx([In] IMFMediaType pMFType, [In] WaveFormat pWaveFormat, [In] int cbBufSize);
+
+ ///
+ /// Converts a Media Foundation audio media type to a WAVEFORMATEX structure.
+ ///
+ /// TODO: try making second parameter out WaveFormatExtraData
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ internal static extern void MFCreateWaveFormatExFromMFMediaType(IMFMediaType pMFType, ref IntPtr ppWF, ref int pcbSize, int flags = 0);
+
+ ///
+ /// Creates the source reader from a URL.
+ ///
+ [DllImport("mfreadwrite.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFCreateSourceReaderFromURL([In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, [In] IMFAttributes pAttributes,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IMFSourceReader ppSourceReader);
+
+ ///
+ /// Creates the source reader from a byte stream.
+ ///
+ [DllImport("mfreadwrite.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFCreateSourceReaderFromByteStream([In] IMFByteStream pByteStream, [In] IMFAttributes pAttributes, [Out, MarshalAs(UnmanagedType.Interface)] out IMFSourceReader ppSourceReader);
+
+ ///
+ /// Creates the sink writer from a URL or byte stream.
+ ///
+ [DllImport("mfreadwrite.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFCreateSinkWriterFromURL([In, MarshalAs(UnmanagedType.LPWStr)] string pwszOutputURL,
+ [In] IMFByteStream pByteStream, [In] IMFAttributes pAttributes, [Out] out IMFSinkWriter ppSinkWriter);
+
+ ///
+ /// Creates a Microsoft Media Foundation byte stream that wraps an IRandomAccessStream object.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFCreateMFByteStreamOnStreamEx([MarshalAs(UnmanagedType.IUnknown)] object punkStream, out IMFByteStream ppByteStream);
+
+#if !NETFX_CORE
+ ///
+ /// Gets a list of Microsoft Media Foundation transforms (MFTs) that match specified search criteria.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFTEnumEx([In] Guid guidCategory, [In] _MFT_ENUM_FLAG flags, [In] MFT_REGISTER_TYPE_INFO pInputType, [In] MFT_REGISTER_TYPE_INFO pOutputType,
+ [Out] out IntPtr pppMFTActivate, [Out] out int pcMFTActivate);
+#endif
+
+ ///
+ /// Creates an empty media sample.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ internal static extern void MFCreateSample([Out] out IMFSample ppIMFSample);
+
+ ///
+ /// Allocates system memory and creates a media buffer to manage it.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ internal static extern void MFCreateMemoryBuffer(
+ int cbMaxLength, [Out] out IMFMediaBuffer ppBuffer);
+
+ ///
+ /// Creates an empty attribute store.
+ ///
+ [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)]
+ internal static extern void MFCreateAttributes(
+ [Out, MarshalAs(UnmanagedType.Interface)] out IMFAttributes ppMFAttributes,
+ [In] int cInitialSize);
+
+#if !NETFX_CORE
+ ///
+ /// Gets a list of output formats from an audio encoder.
+ ///
+ [DllImport("mf.dll", ExactSpelling = true, PreserveSig = false)]
+ public static extern void MFTranscodeGetAudioOutputAvailableTypes(
+ [In, MarshalAs(UnmanagedType.LPStruct)] Guid guidSubType,
+ [In] _MFT_ENUM_FLAG dwMFTFlags,
+ [In] IMFAttributes pCodecConfig,
+ [Out, MarshalAs(UnmanagedType.Interface)] out IMFCollection ppAvailableTypes);
+#endif
+
+ ///
+ /// All streams
+ ///
+ public const int MF_SOURCE_READER_ALL_STREAMS = unchecked((int)0xFFFFFFFE);
+ ///
+ /// First audio stream
+ ///
+ public const int MF_SOURCE_READER_FIRST_AUDIO_STREAM = unchecked((int)0xFFFFFFFD);
+ ///
+ /// First video stream
+ ///
+ public const int MF_SOURCE_READER_FIRST_VIDEO_STREAM = unchecked((int)0xFFFFFFFC);
+ ///
+ /// Media source
+ ///
+ public const int MF_SOURCE_READER_MEDIASOURCE = unchecked((int)0xFFFFFFFF);
+ ///
+ /// Media Foundation SDK Version
+ ///
+ public const int MF_SDK_VERSION = 0x2;
+ ///
+ /// Media Foundation API Version
+ ///
+ public const int MF_API_VERSION = 0x70;
+ ///
+ /// Media Foundation Version
+ ///
+ public const int MF_VERSION = (MF_SDK_VERSION << 16) | MF_API_VERSION;
+
+
+ }
+}
diff --git a/NAudio/MediaFoundation/MediaFoundationTransform.cs b/NAudio/MediaFoundation/MediaFoundationTransform.cs
new file mode 100644
index 00000000..576facb9
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaFoundationTransform.cs
@@ -0,0 +1,286 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Utils;
+using NAudio.Wave;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// An abstract base class for simplifying working with Media Foundation Transforms
+ /// You need to override the method that actually creates and configures the transform
+ ///
+ public abstract class MediaFoundationTransform : IWaveProvider, IDisposable
+ {
+ ///
+ /// The Source Provider
+ ///
+ protected readonly IWaveProvider sourceProvider;
+ ///
+ /// The Output WaveFormat
+ ///
+ protected readonly WaveFormat outputWaveFormat;
+ private readonly byte[] sourceBuffer;
+
+ private byte[] outputBuffer;
+ private int outputBufferOffset;
+ private int outputBufferCount;
+
+ private IMFTransform transform;
+ private bool disposed;
+ private long inputPosition; // in ref-time, so we can timestamp the input samples
+ private long outputPosition; // also in ref-time
+ private bool initializedForStreaming;
+
+ ///
+ /// Constructs a new MediaFoundationTransform wrapper
+ /// Will read one second at a time
+ ///
+ /// The source provider for input data to the transform
+ /// The desired output format
+ public MediaFoundationTransform(IWaveProvider sourceProvider, WaveFormat outputFormat)
+ {
+ this.outputWaveFormat = outputFormat;
+ this.sourceProvider = sourceProvider;
+ sourceBuffer = new byte[sourceProvider.WaveFormat.AverageBytesPerSecond];
+ outputBuffer = new byte[outputWaveFormat.AverageBytesPerSecond + outputWaveFormat.BlockAlign]; // we will grow this buffer if needed, but try to make something big enough
+ }
+
+ private void InitializeTransformForStreaming()
+ {
+ transform.ProcessMessage(MFT_MESSAGE_TYPE.MFT_MESSAGE_COMMAND_FLUSH, IntPtr.Zero);
+ transform.ProcessMessage(MFT_MESSAGE_TYPE.MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, IntPtr.Zero);
+ transform.ProcessMessage(MFT_MESSAGE_TYPE.MFT_MESSAGE_NOTIFY_START_OF_STREAM, IntPtr.Zero);
+ initializedForStreaming = true;
+ }
+
+ ///
+ /// To be implemented by overriding classes. Create the transform object, set up its input and output types,
+ /// and configure any custom properties in here
+ ///
+ /// An object implementing IMFTrasform
+ protected abstract IMFTransform CreateTransform();
+
+ ///
+ /// Disposes this MediaFoundation transform
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (transform != null)
+ {
+ Marshal.ReleaseComObject(transform);
+ }
+ }
+
+ ///
+ /// Disposes this Media Foundation Transform
+ ///
+ public void Dispose()
+ {
+ if (!disposed)
+ {
+ disposed = true;
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ ///
+ /// Destructor
+ ///
+ ~MediaFoundationTransform()
+ {
+ Dispose(false);
+ }
+
+ ///
+ /// The output WaveFormat of this Media Foundation Transform
+ ///
+ public WaveFormat WaveFormat { get { return outputWaveFormat; } }
+
+ ///
+ /// Reads data out of the source, passing it through the transform
+ ///
+ /// Output buffer
+ /// Offset within buffer to write to
+ /// Desired byte count
+ /// Number of bytes read
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ if (transform == null)
+ {
+ transform = CreateTransform();
+ InitializeTransformForStreaming();
+ }
+
+ // strategy will be to always read 1 second from the source, and give it to the resampler
+ int bytesWritten = 0;
+
+ // read in any leftovers from last time
+ if (outputBufferCount > 0)
+ {
+ bytesWritten += ReadFromOutputBuffer(buffer, offset, count - bytesWritten);
+ }
+
+ while (bytesWritten < count)
+ {
+ var sample = ReadFromSource();
+ if (sample == null) // reached the end of our input
+ {
+ // be good citizens and send some end messages:
+ EndStreamAndDrain();
+ // resampler might have given us a little bit more to return
+ bytesWritten += ReadFromOutputBuffer(buffer, offset + bytesWritten, count - bytesWritten);
+ break;
+ }
+
+ // might need to resurrect the stream if the user has read all the way to the end,
+ // and then repositioned the input backwards
+ if (!initializedForStreaming)
+ {
+ InitializeTransformForStreaming();
+ }
+
+ // give the input to the resampler
+ // can get MF_E_NOTACCEPTING if we didn't drain the buffer properly
+ transform.ProcessInput(0, sample, 0);
+
+ Marshal.ReleaseComObject(sample);
+
+ int readFromTransform;
+ // n.b. in theory we ought to loop here, although we'd need to be careful as the next time into ReadFromTransform there could
+ // still be some leftover bytes in outputBuffer, which would get overwritten. Only introduce this if we find a transform that
+ // needs it. For most transforms, alternating read/write should be OK
+ //do
+ //{
+ // keep reading from transform
+ readFromTransform = ReadFromTransform();
+ bytesWritten += ReadFromOutputBuffer(buffer, offset + bytesWritten, count - bytesWritten);
+ //} while (readFromTransform > 0);
+ }
+
+ return bytesWritten;
+ }
+
+ private void EndStreamAndDrain()
+ {
+ transform.ProcessMessage(MFT_MESSAGE_TYPE.MFT_MESSAGE_NOTIFY_END_OF_STREAM, IntPtr.Zero);
+ transform.ProcessMessage(MFT_MESSAGE_TYPE.MFT_MESSAGE_COMMAND_DRAIN, IntPtr.Zero);
+ int read;
+ do
+ {
+ read = ReadFromTransform();
+ } while (read > 0);
+ outputBufferCount = 0;
+ outputBufferOffset = 0;
+ inputPosition = 0;
+ outputPosition = 0;
+ transform.ProcessMessage(MFT_MESSAGE_TYPE.MFT_MESSAGE_NOTIFY_END_STREAMING, IntPtr.Zero);
+ initializedForStreaming = false;
+ }
+
+ ///
+ /// Attempts to read from the transform
+ /// Some useful info here:
+ /// http://msdn.microsoft.com/en-gb/library/windows/desktop/aa965264%28v=vs.85%29.aspx#process_data
+ ///
+ ///
+ private int ReadFromTransform()
+ {
+ var outputDataBuffer = new MFT_OUTPUT_DATA_BUFFER[1];
+ // we have to create our own for
+ var sample = MediaFoundationApi.CreateSample();
+ var pBuffer = MediaFoundationApi.CreateMemoryBuffer(outputBuffer.Length);
+ sample.AddBuffer(pBuffer);
+ sample.SetSampleTime(outputPosition); // hopefully this is not needed
+ outputDataBuffer[0].pSample = sample;
+
+ _MFT_PROCESS_OUTPUT_STATUS status;
+ var hr = transform.ProcessOutput(_MFT_PROCESS_OUTPUT_FLAGS.None,
+ 1, outputDataBuffer, out status);
+ if (hr == MediaFoundationErrors.MF_E_TRANSFORM_NEED_MORE_INPUT)
+ {
+ Marshal.ReleaseComObject(pBuffer);
+ Marshal.ReleaseComObject(sample);
+ // nothing to read
+ return 0;
+ }
+ else if (hr != 0)
+ {
+ Marshal.ThrowExceptionForHR(hr);
+ }
+
+ IMFMediaBuffer outputMediaBuffer;
+ outputDataBuffer[0].pSample.ConvertToContiguousBuffer(out outputMediaBuffer);
+ IntPtr pOutputBuffer;
+ int outputBufferLength;
+ int maxSize;
+ outputMediaBuffer.Lock(out pOutputBuffer, out maxSize, out outputBufferLength);
+ outputBuffer = BufferHelpers.Ensure(outputBuffer, outputBufferLength);
+ Marshal.Copy(pOutputBuffer, outputBuffer, 0, outputBufferLength);
+ outputBufferOffset = 0;
+ outputBufferCount = outputBufferLength;
+ outputMediaBuffer.Unlock();
+ outputPosition += BytesToNsPosition(outputBufferCount, WaveFormat); // hopefully not needed
+ Marshal.ReleaseComObject(pBuffer);
+ Marshal.ReleaseComObject(sample);
+ Marshal.ReleaseComObject(outputMediaBuffer);
+ return outputBufferLength;
+ }
+
+ private static long BytesToNsPosition(int bytes, WaveFormat waveFormat)
+ {
+ long nsPosition = (10000000L * bytes) / waveFormat.AverageBytesPerSecond;
+ return nsPosition;
+ }
+
+ private IMFSample ReadFromSource()
+ {
+ // we always read a full second
+ int bytesRead = sourceProvider.Read(sourceBuffer, 0, sourceBuffer.Length);
+ if (bytesRead == 0) return null;
+
+ var mediaBuffer = MediaFoundationApi.CreateMemoryBuffer(bytesRead);
+ IntPtr pBuffer;
+ int maxLength, currentLength;
+ mediaBuffer.Lock(out pBuffer, out maxLength, out currentLength);
+ Marshal.Copy(sourceBuffer, 0, pBuffer, bytesRead);
+ mediaBuffer.Unlock();
+ mediaBuffer.SetCurrentLength(bytesRead);
+
+ var sample = MediaFoundationApi.CreateSample();
+ sample.AddBuffer(mediaBuffer);
+ // we'll set the time, I don't think it is needed for Resampler, but other MFTs might need it
+ sample.SetSampleTime(inputPosition);
+ long duration = BytesToNsPosition(bytesRead, sourceProvider.WaveFormat);
+ sample.SetSampleDuration(duration);
+ inputPosition += duration;
+ Marshal.ReleaseComObject(mediaBuffer);
+ return sample;
+ }
+
+ private int ReadFromOutputBuffer(byte[] buffer, int offset, int needed)
+ {
+ int bytesFromOutputBuffer = Math.Min(needed, outputBufferCount);
+ Array.Copy(outputBuffer, outputBufferOffset, buffer, offset, bytesFromOutputBuffer);
+ outputBufferOffset += bytesFromOutputBuffer;
+ outputBufferCount -= bytesFromOutputBuffer;
+ if (outputBufferCount == 0)
+ {
+ outputBufferOffset = 0;
+ }
+ return bytesFromOutputBuffer;
+ }
+
+ ///
+ /// Indicate that the source has been repositioned and completely drain out the transforms buffers
+ ///
+ public void Reposition()
+ {
+ if (initializedForStreaming)
+ {
+ EndStreamAndDrain();
+ InitializeTransformForStreaming();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/MediaFoundationTransformCategories.cs b/NAudio/MediaFoundation/MediaFoundationTransformCategories.cs
new file mode 100644
index 00000000..9989eb44
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaFoundationTransformCategories.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using NAudio.Utils;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Media Foundation Transform Categories
+ ///
+ public static class MediaFoundationTransformCategories
+ {
+ ///
+ /// MFT_CATEGORY_VIDEO_DECODER
+ ///
+ [FieldDescription("Video Decoder")]
+ public static readonly Guid VideoDecoder = new Guid("{d6c02d4b-6833-45b4-971a-05a4b04bab91}");
+ ///
+ /// MFT_CATEGORY_VIDEO_ENCODER
+ ///
+ [FieldDescription("Video Encoder")]
+ public static readonly Guid VideoEncoder = new Guid("{f79eac7d-e545-4387-bdee-d647d7bde42a}");
+ ///
+ /// MFT_CATEGORY_VIDEO_EFFECT
+ ///
+ [FieldDescription("Video Effect")]
+ public static readonly Guid VideoEffect = new Guid("{12e17c21-532c-4a6e-8a1c-40825a736397}");
+ ///
+ /// MFT_CATEGORY_MULTIPLEXER
+ ///
+ [FieldDescription("Multiplexer")]
+ public static readonly Guid Multiplexer = new Guid("{059c561e-05ae-4b61-b69d-55b61ee54a7b}");
+ ///
+ /// MFT_CATEGORY_DEMULTIPLEXER
+ ///
+ [FieldDescription("Demultiplexer")]
+ public static readonly Guid Demultiplexer = new Guid("{a8700a7a-939b-44c5-99d7-76226b23b3f1}");
+ ///
+ /// MFT_CATEGORY_AUDIO_DECODER
+ ///
+ [FieldDescription("Audio Decoder")]
+ public static readonly Guid AudioDecoder = new Guid("{9ea73fb4-ef7a-4559-8d5d-719d8f0426c7}");
+ ///
+ /// MFT_CATEGORY_AUDIO_ENCODER
+ ///
+ [FieldDescription("Audio Encoder")]
+ public static readonly Guid AudioEncoder = new Guid("{91c64bd0-f91e-4d8c-9276-db248279d975}");
+ ///
+ /// MFT_CATEGORY_AUDIO_EFFECT
+ ///
+ [FieldDescription("Audio Effect")]
+ public static readonly Guid AudioEffect = new Guid("{11064c48-3648-4ed0-932e-05ce8ac811b7}");
+ ///
+ /// MFT_CATEGORY_VIDEO_PROCESSOR
+ ///
+ [FieldDescription("Video Processor")]
+ public static readonly Guid VideoProcessor = new Guid("{302EA3FC-AA5F-47f9-9F7A-C2188BB16302}");
+ ///
+ /// MFT_CATEGORY_OTHER
+ ///
+ [FieldDescription("Other")]
+ public static readonly Guid Other = new Guid("{90175d57-b7ea-4901-aeb3-933a8747756f}");
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/MediaType.cs b/NAudio/MediaFoundation/MediaType.cs
new file mode 100644
index 00000000..54b28206
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaType.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Utils;
+using NAudio.Wave;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Media Type helper class, simplifying working with IMFMediaType
+ /// (will probably change in the future, to inherit from an attributes class)
+ /// Currently does not release the COM object, so you must do that yourself
+ ///
+ public class MediaType
+ {
+ private readonly IMFMediaType mediaType;
+
+ ///
+ /// Wraps an existing IMFMediaType object
+ ///
+ /// The IMFMediaType object
+ public MediaType(IMFMediaType mediaType)
+ {
+ this.mediaType = mediaType;
+ }
+
+ ///
+ /// Creates and wraps a new IMFMediaType object
+ ///
+ public MediaType()
+ {
+ mediaType = MediaFoundationApi.CreateMediaType();
+ }
+
+ ///
+ /// Creates and wraps a new IMFMediaType object based on a WaveFormat
+ ///
+ /// WaveFormat
+ public MediaType(WaveFormat waveFormat)
+ {
+ mediaType = MediaFoundationApi.CreateMediaTypeFromWaveFormat(waveFormat);
+ }
+
+ private int GetUInt32(Guid key)
+ {
+ int value;
+ mediaType.GetUINT32(key, out value);
+ return value;
+ }
+
+ private Guid GetGuid(Guid key)
+ {
+ Guid value;
+ mediaType.GetGUID(key, out value);
+ return value;
+ }
+
+ ///
+ /// Tries to get a UINT32 value, returning a default value if it doesn't exist
+ ///
+ /// Attribute key
+ /// Default value
+ /// Value or default if key doesn't exist
+ public int TryGetUInt32(Guid key, int defaultValue = -1)
+ {
+ int intValue = defaultValue;
+ try
+ {
+ mediaType.GetUINT32(key, out intValue);
+ }
+ catch (COMException exception)
+ {
+ if (exception.GetHResult() == MediaFoundationErrors.MF_E_ATTRIBUTENOTFOUND)
+ {
+ // not a problem, return the default
+ }
+ else if (exception.GetHResult() == MediaFoundationErrors.MF_E_INVALIDTYPE)
+ {
+ throw new ArgumentException("Not a UINT32 parameter");
+ }
+ else
+ {
+ throw;
+ }
+ }
+ return intValue;
+ }
+
+ ///
+ /// The Sample Rate (valid for audio media types)
+ ///
+ public int SampleRate
+ {
+ get { return GetUInt32(MediaFoundationAttributes.MF_MT_AUDIO_SAMPLES_PER_SECOND); }
+ set { mediaType.SetUINT32(MediaFoundationAttributes.MF_MT_AUDIO_SAMPLES_PER_SECOND, value); }
+ }
+
+ ///
+ /// The number of Channels (valid for audio media types)
+ ///
+ public int ChannelCount
+ {
+ get { return GetUInt32(MediaFoundationAttributes.MF_MT_AUDIO_NUM_CHANNELS); }
+ set { mediaType.SetUINT32(MediaFoundationAttributes.MF_MT_AUDIO_NUM_CHANNELS, value); }
+ }
+
+ ///
+ /// The number of bits per sample (n.b. not always valid for compressed audio types)
+ ///
+ public int BitsPerSample
+ {
+ get { return GetUInt32(MediaFoundationAttributes.MF_MT_AUDIO_BITS_PER_SAMPLE); }
+ set { mediaType.SetUINT32(MediaFoundationAttributes.MF_MT_AUDIO_BITS_PER_SAMPLE, value); }
+ }
+
+ ///
+ /// The average bytes per second (valid for audio media types)
+ ///
+ public int AverageBytesPerSecond
+ {
+ get { return GetUInt32(MediaFoundationAttributes.MF_MT_AUDIO_AVG_BYTES_PER_SECOND); }
+ }
+
+ ///
+ /// The Media Subtype. For audio, is a value from the AudioSubtypes class
+ ///
+ public Guid SubType
+ {
+ get { return GetGuid(MediaFoundationAttributes.MF_MT_SUBTYPE); }
+ set { mediaType.SetGUID(MediaFoundationAttributes.MF_MT_SUBTYPE, value); }
+ }
+
+ ///
+ /// The Major type, e.g. audio or video (from the MediaTypes class)
+ ///
+ public Guid MajorType
+ {
+ get { return GetGuid(MediaFoundationAttributes.MF_MT_MAJOR_TYPE); }
+ set { mediaType.SetGUID(MediaFoundationAttributes.MF_MT_MAJOR_TYPE, value); }
+ }
+
+ ///
+ /// Access to the actual IMFMediaType object
+ /// Use to pass to MF APIs or Marshal.ReleaseComObject when you are finished with it
+ ///
+ public IMFMediaType MediaFoundationObject
+ {
+ get { return mediaType; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/MediaTypes.cs b/NAudio/MediaFoundation/MediaTypes.cs
new file mode 100644
index 00000000..ea9bed79
--- /dev/null
+++ b/NAudio/MediaFoundation/MediaTypes.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Major Media Types
+ /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa367377%28v=vs.85%29.aspx
+ ///
+ public static class MediaTypes
+ {
+ ///
+ /// Default
+ ///
+ public static readonly Guid MFMediaType_Default = new Guid("81A412E6-8103-4B06-857F-1862781024AC");
+ ///
+ /// Audio
+ ///
+ [FieldDescription("Audio")]
+ public static readonly Guid MFMediaType_Audio = new Guid("73647561-0000-0010-8000-00aa00389b71");
+ ///
+ /// Video
+ ///
+ [FieldDescription("Video")]
+ public static readonly Guid MFMediaType_Video = new Guid("73646976-0000-0010-8000-00aa00389b71");
+ ///
+ /// Protected Media
+ ///
+ [FieldDescription("Protected Media")]
+ public static readonly Guid MFMediaType_Protected = new Guid("7b4b6fe6-9d04-4494-be14-7e0bd076c8e4");
+ ///
+ /// Synchronized Accessible Media Interchange (SAMI) captions.
+ ///
+ [FieldDescription("SAMI captions")]
+ public static readonly Guid MFMediaType_SAMI = new Guid("e69669a0-3dcd-40cb-9e2e-3708387c0616");
+ ///
+ /// Script stream
+ ///
+ [FieldDescription("Script stream")]
+ public static readonly Guid MFMediaType_Script = new Guid("72178c22-e45b-11d5-bc2a-00b0d0f3f4ab");
+ ///
+ /// Still image stream.
+ ///
+ [FieldDescription("Still image stream")]
+ public static readonly Guid MFMediaType_Image = new Guid("72178c23-e45b-11d5-bc2a-00b0d0f3f4ab");
+ ///
+ /// HTML stream.
+ ///
+ [FieldDescription("HTML stream")]
+ public static readonly Guid MFMediaType_HTML = new Guid("72178c24-e45b-11d5-bc2a-00b0d0f3f4ab");
+ ///
+ /// Binary stream.
+ ///
+ [FieldDescription("Binary stream")]
+ public static readonly Guid MFMediaType_Binary = new Guid("72178c25-e45b-11d5-bc2a-00b0d0f3f4ab");
+ ///
+ /// A stream that contains data files.
+ ///
+ [FieldDescription("File transfer")]
+ public static readonly Guid MFMediaType_FileTransfer = new Guid("72178c26-e45b-11d5-bc2a-00b0d0f3f4ab");
+ }
+}
diff --git a/NAudio/MediaFoundation/_MFT_ENUM_FLAG.cs b/NAudio/MediaFoundation/_MFT_ENUM_FLAG.cs
new file mode 100644
index 00000000..189dbc71
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_ENUM_FLAG.cs
@@ -0,0 +1,48 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Contains flags for registering and enumeration Media Foundation transforms (MFTs).
+ ///
+ [Flags]
+ public enum _MFT_ENUM_FLAG
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// The MFT performs synchronous data processing in software.
+ ///
+ MFT_ENUM_FLAG_SYNCMFT = 0x00000001,
+ ///
+ /// The MFT performs asynchronous data processing in software.
+ ///
+ MFT_ENUM_FLAG_ASYNCMFT = 0x00000002,
+ ///
+ /// The MFT performs hardware-based data processing, using either the AVStream driver or a GPU-based proxy MFT.
+ ///
+ MFT_ENUM_FLAG_HARDWARE = 0x00000004,
+ ///
+ /// The MFT that must be unlocked by the application before use.
+ ///
+ MFT_ENUM_FLAG_FIELDOFUSE = 0x00000008,
+ ///
+ /// For enumeration, include MFTs that were registered in the caller's process.
+ ///
+ MFT_ENUM_FLAG_LOCALMFT = 0x00000010,
+ ///
+ /// The MFT is optimized for transcoding rather than playback.
+ ///
+ MFT_ENUM_FLAG_TRANSCODE_ONLY = 0x00000020,
+ ///
+ /// For enumeration, sort and filter the results.
+ ///
+ MFT_ENUM_FLAG_SORTANDFILTER = 0x00000040,
+ ///
+ /// Bitwise OR of all the flags, excluding MFT_ENUM_FLAG_SORTANDFILTER.
+ ///
+ MFT_ENUM_FLAG_ALL = 0x0000003F
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_INPUT_STATUS_FLAGS.cs b/NAudio/MediaFoundation/_MFT_INPUT_STATUS_FLAGS.cs
new file mode 100644
index 00000000..a2d3c48a
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_INPUT_STATUS_FLAGS.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Indicates the status of an input stream on a Media Foundation transform (MFT).
+ ///
+ [Flags]
+ public enum _MFT_INPUT_STATUS_FLAGS
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// The input stream can receive more data at this time.
+ ///
+ MFT_INPUT_STATUS_ACCEPT_DATA = 0x00000001
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_INPUT_STREAM_INFO_FLAGS.cs b/NAudio/MediaFoundation/_MFT_INPUT_STREAM_INFO_FLAGS.cs
new file mode 100644
index 00000000..5dff5dcf
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_INPUT_STREAM_INFO_FLAGS.cs
@@ -0,0 +1,48 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Describes an input stream on a Media Foundation transform (MFT).
+ ///
+ [Flags]
+ public enum _MFT_INPUT_STREAM_INFO_FLAGS
+ {
+ ///
+ /// No flags set
+ ///
+ None = 0,
+ ///
+ /// Each media sample (IMFSample interface) of input data must contain complete, unbroken units of data.
+ ///
+ MFT_INPUT_STREAM_WHOLE_SAMPLES = 0x00000001,
+ ///
+ /// Each media sample that the client provides as input must contain exactly one unit of data, as defined for the MFT_INPUT_STREAM_WHOLE_SAMPLES flag.
+ ///
+ MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER = 0x00000002,
+ ///
+ /// All input samples must be the same size.
+ ///
+ MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE = 0x00000004,
+ ///
+ /// MTF Input Stream Holds buffers
+ ///
+ MFT_INPUT_STREAM_HOLDS_BUFFERS = 0x00000008,
+ ///
+ /// The MFT does not hold input samples after the IMFTransform::ProcessInput method returns.
+ ///
+ MFT_INPUT_STREAM_DOES_NOT_ADDREF = 0x00000100,
+ ///
+ /// This input stream can be removed by calling IMFTransform::DeleteInputStream.
+ ///
+ MFT_INPUT_STREAM_REMOVABLE = 0x00000200,
+ ///
+ /// This input stream is optional.
+ ///
+ MFT_INPUT_STREAM_OPTIONAL = 0x00000400,
+ ///
+ /// The MFT can perform in-place processing.
+ ///
+ MFT_INPUT_STREAM_PROCESSES_IN_PLACE = 0x00000800
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_OUTPUT_DATA_BUFFER_FLAGS.cs b/NAudio/MediaFoundation/_MFT_OUTPUT_DATA_BUFFER_FLAGS.cs
new file mode 100644
index 00000000..8128114a
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_OUTPUT_DATA_BUFFER_FLAGS.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Defines flags for the IMFTransform::ProcessOutput method.
+ ///
+ [Flags]
+ public enum _MFT_OUTPUT_DATA_BUFFER_FLAGS
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// The MFT can still generate output from this stream without receiving any more input.
+ ///
+ MFT_OUTPUT_DATA_BUFFER_INCOMPLETE = 0x01000000,
+ ///
+ /// The format has changed on this output stream, or there is a new preferred format for this stream.
+ ///
+ MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE = 0x00000100,
+ ///
+ /// The MFT has removed this output stream.
+ ///
+ MFT_OUTPUT_DATA_BUFFER_STREAM_END = 0x00000200,
+ ///
+ /// There is no sample ready for this stream.
+ ///
+ MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE = 0x00000300
+
+ };
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_OUTPUT_STATUS_FLAGS.cs b/NAudio/MediaFoundation/_MFT_OUTPUT_STATUS_FLAGS.cs
new file mode 100644
index 00000000..31bab646
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_OUTPUT_STATUS_FLAGS.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Indicates whether a Media Foundation transform (MFT) can produce output data.
+ ///
+ [Flags]
+ public enum _MFT_OUTPUT_STATUS_FLAGS
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// There is a sample available for at least one output stream.
+ ///
+ MFT_OUTPUT_STATUS_SAMPLE_READY = 0x00000001
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_OUTPUT_STREAM_INFO_FLAGS.cs b/NAudio/MediaFoundation/_MFT_OUTPUT_STREAM_INFO_FLAGS.cs
new file mode 100644
index 00000000..029baaf1
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_OUTPUT_STREAM_INFO_FLAGS.cs
@@ -0,0 +1,52 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Describes an output stream on a Media Foundation transform (MFT).
+ ///
+ [Flags]
+ public enum _MFT_OUTPUT_STREAM_INFO_FLAGS
+ {
+ ///
+ /// No flags set
+ ///
+ None = 0,
+ ///
+ /// Each media sample (IMFSample interface) of output data from the MFT contains complete, unbroken units of data.
+ ///
+ MFT_OUTPUT_STREAM_WHOLE_SAMPLES = 0x00000001,
+ ///
+ /// Each output sample contains exactly one unit of data, as defined for the MFT_OUTPUT_STREAM_WHOLE_SAMPLES flag.
+ ///
+ MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER = 0x00000002,
+ ///
+ /// All output samples are the same size.
+ ///
+ MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE = 0x00000004,
+ ///
+ /// The MFT can discard the output data from this output stream, if requested by the client.
+ ///
+ MFT_OUTPUT_STREAM_DISCARDABLE = 0x00000008,
+ ///
+ /// This output stream is optional.
+ ///
+ MFT_OUTPUT_STREAM_OPTIONAL = 0x00000010,
+ ///
+ /// The MFT provides the output samples for this stream, either by allocating them internally or by operating directly on the input samples.
+ ///
+ MFT_OUTPUT_STREAM_PROVIDES_SAMPLES = 0x00000100,
+ ///
+ /// The MFT can either provide output samples for this stream or it can use samples that the client allocates.
+ ///
+ MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES = 0x00000200,
+ ///
+ /// The MFT does not require the client to process the output for this stream.
+ ///
+ MFT_OUTPUT_STREAM_LAZY_READ = 0x00000400,
+ ///
+ /// The MFT might remove this output stream during streaming.
+ ///
+ MFT_OUTPUT_STREAM_REMOVABLE = 0x00000800
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_PROCESS_OUTPUT_FLAGS.cs b/NAudio/MediaFoundation/_MFT_PROCESS_OUTPUT_FLAGS.cs
new file mode 100644
index 00000000..f5a1dbf3
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_PROCESS_OUTPUT_FLAGS.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Defines flags for processing output samples in a Media Foundation transform (MFT).
+ ///
+ [Flags]
+ public enum _MFT_PROCESS_OUTPUT_FLAGS
+ {
+ ///
+ /// None
+ ///
+ None,
+ ///
+ /// Do not produce output for streams in which the pSample member of the MFT_OUTPUT_DATA_BUFFER structure is NULL.
+ ///
+ MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER = 0x00000001,
+ ///
+ /// Regenerates the last output sample.
+ ///
+ MFT_PROCESS_OUTPUT_REGENERATE_LAST_OUTPUT = 0x00000002
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_PROCESS_OUTPUT_STATUS.cs b/NAudio/MediaFoundation/_MFT_PROCESS_OUTPUT_STATUS.cs
new file mode 100644
index 00000000..8d5d2697
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_PROCESS_OUTPUT_STATUS.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Process Output Status flags
+ ///
+ [Flags]
+ public enum _MFT_PROCESS_OUTPUT_STATUS
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// The Media Foundation transform (MFT) has created one or more new output streams.
+ ///
+ MFT_PROCESS_OUTPUT_STATUS_NEW_STREAMS = 0x00000100
+ }
+}
\ No newline at end of file
diff --git a/NAudio/MediaFoundation/_MFT_SET_TYPE_FLAGS.cs b/NAudio/MediaFoundation/_MFT_SET_TYPE_FLAGS.cs
new file mode 100644
index 00000000..7f7f55b7
--- /dev/null
+++ b/NAudio/MediaFoundation/_MFT_SET_TYPE_FLAGS.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace NAudio.MediaFoundation
+{
+ ///
+ /// Defines flags for the setting or testing the media type on a Media Foundation transform (MFT).
+ ///
+ [Flags]
+ public enum _MFT_SET_TYPE_FLAGS
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// Test the proposed media type, but do not set it.
+ ///
+ MFT_SET_TYPE_TEST_ONLY = 0x00000001
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/ChannelAfterTouchEvent.cs b/NAudio/Midi/ChannelAfterTouchEvent.cs
new file mode 100644
index 00000000..f2f12bee
--- /dev/null
+++ b/NAudio/Midi/ChannelAfterTouchEvent.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI Channel AfterTouch Event.
+ ///
+ public class ChannelAfterTouchEvent : MidiEvent
+ {
+ private byte afterTouchPressure;
+
+ ///
+ /// Creates a new ChannelAfterTouchEvent from raw MIDI data
+ ///
+ /// A binary reader
+ public ChannelAfterTouchEvent(BinaryReader br)
+ {
+ afterTouchPressure = br.ReadByte();
+ if ((afterTouchPressure & 0x80) != 0)
+ {
+ // TODO: might be a follow-on
+ throw new FormatException("Invalid afterTouchPressure");
+ }
+ }
+
+ ///
+ /// Creates a new Channel After-Touch Event
+ ///
+ /// Absolute time
+ /// Channel
+ /// After-touch pressure
+ public ChannelAfterTouchEvent(long absoluteTime, int channel, int afterTouchPressure)
+ : base(absoluteTime, channel, MidiCommandCode.ChannelAfterTouch)
+ {
+ AfterTouchPressure = afterTouchPressure;
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write(afterTouchPressure);
+ }
+
+ ///
+ /// The aftertouch pressure value
+ ///
+ public int AfterTouchPressure
+ {
+ get { return afterTouchPressure; }
+ set
+ {
+ if (value < 0 || value > 127)
+ {
+ throw new ArgumentOutOfRangeException("value", "After touch pressure must be in the range 0-127");
+ }
+ afterTouchPressure = (byte) value;
+ }
+ }
+ }
+}
diff --git a/NAudio/Midi/ControlChangeEvent.cs b/NAudio/Midi/ControlChangeEvent.cs
new file mode 100644
index 00000000..d2e979f0
--- /dev/null
+++ b/NAudio/Midi/ControlChangeEvent.cs
@@ -0,0 +1,120 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI control change event
+ ///
+ public class ControlChangeEvent : MidiEvent
+ {
+ private MidiController controller;
+ private byte controllerValue;
+
+ ///
+ /// Reads a control change event from a MIDI stream
+ ///
+ /// Binary reader on the MIDI stream
+ public ControlChangeEvent(BinaryReader br)
+ {
+ byte c = br.ReadByte();
+ controllerValue = br.ReadByte();
+ if((c & 0x80) != 0)
+ {
+ // TODO: might be a follow-on
+ throw new InvalidDataException("Invalid controller");
+ }
+ controller = (MidiController) c;
+ if((controllerValue & 0x80) != 0)
+ {
+ throw new InvalidDataException(String.Format("Invalid controllerValue {0} for controller {1}, Pos 0x{2:X}", controllerValue, controller, br.BaseStream.Position));
+ }
+ }
+
+ ///
+ /// Creates a control change event
+ ///
+ /// Time
+ /// MIDI Channel Number
+ /// The MIDI Controller
+ /// Controller value
+ public ControlChangeEvent(long absoluteTime, int channel, MidiController controller, int controllerValue)
+ : base(absoluteTime,channel,MidiCommandCode.ControlChange)
+ {
+ this.Controller = controller;
+ this.ControllerValue = controllerValue;
+ }
+
+ ///
+ /// Describes this control change event
+ ///
+ /// A string describing this event
+ public override string ToString()
+ {
+ return String.Format("{0} Controller {1} Value {2}",
+ base.ToString(),
+ this.controller,
+ this.controllerValue);
+ }
+
+ ///
+ ///
+ ///
+ public override int GetAsShortMessage()
+ {
+ byte c = (byte)controller;
+ return base.GetAsShortMessage() + (c << 8) + (controllerValue << 16);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write((byte)controller);
+ writer.Write((byte)controllerValue);
+ }
+
+ ///
+ /// The controller number
+ ///
+ public MidiController Controller
+ {
+ get
+ {
+ return controller;
+ }
+ set
+ {
+ if ((int) value < 0 || (int) value > 127)
+ {
+ throw new ArgumentOutOfRangeException("value", "Controller number must be in the range 0-127");
+ }
+ controller = value;
+ }
+ }
+
+ ///
+ /// The controller value
+ ///
+ public int ControllerValue
+ {
+ get
+ {
+ return controllerValue;
+ }
+ set
+ {
+ if (value < 0 || value > 127)
+ {
+ throw new ArgumentOutOfRangeException("value", "Controller Value must be in the range 0-127");
+ }
+ controllerValue = (byte) value;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/KeySignatureEvent.cs b/NAudio/Midi/KeySignatureEvent.cs
new file mode 100644
index 00000000..a4def916
--- /dev/null
+++ b/NAudio/Midi/KeySignatureEvent.cs
@@ -0,0 +1,85 @@
+using System;
+using System.IO;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI key signature event event
+ ///
+ public class KeySignatureEvent : MetaEvent
+ {
+ private byte sharpsFlats;
+ private byte majorMinor;
+
+ ///
+ /// Reads a new track sequence number event from a MIDI stream
+ ///
+ /// The MIDI stream
+ /// the data length
+ public KeySignatureEvent(BinaryReader br, int length)
+ {
+ if (length != 2)
+ {
+ throw new FormatException("Invalid key signature length");
+ }
+ sharpsFlats = br.ReadByte(); // sf=sharps/flats (-7=7 flats, 0=key of C,7=7 sharps)
+ majorMinor = br.ReadByte(); // mi=major/minor (0=major, 1=minor) }
+ }
+
+ ///
+ /// Creates a new Key signature event with the specified data
+ ///
+ public KeySignatureEvent(int sharpsFlats, int majorMinor, long absoluteTime)
+ : base(MetaEventType.KeySignature, 2, absoluteTime)
+ {
+ this.sharpsFlats = (byte) sharpsFlats;
+ this.majorMinor = (byte) majorMinor;
+ }
+
+ ///
+ /// Number of sharps or flats
+ ///
+ public int SharpsFlats
+ {
+ get
+ {
+ return sharpsFlats;
+ }
+ }
+
+ ///
+ /// Major or Minor key
+ ///
+ public int MajorMinor
+ {
+ get
+ {
+ return majorMinor;
+ }
+ }
+
+ ///
+ /// Describes this event
+ ///
+ /// String describing the event
+ public override string ToString()
+ {
+ return String.Format("{0} {1} {2}", base.ToString(), sharpsFlats, majorMinor);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write(sharpsFlats);
+ writer.Write(majorMinor);
+ }
+ }
+}
+
+
+
diff --git a/NAudio/Midi/MetaEvent.cs b/NAudio/Midi/MetaEvent.cs
new file mode 100644
index 00000000..b2c024ff
--- /dev/null
+++ b/NAudio/Midi/MetaEvent.cs
@@ -0,0 +1,139 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI meta event
+ ///
+ public class MetaEvent : MidiEvent
+ {
+ private MetaEventType metaEvent;
+ internal int metaDataLength;
+ private byte[] data; // only filled in for generic meta-event types
+ ///
+ /// Gets the type of this meta event
+ ///
+ public MetaEventType MetaEventType
+ {
+ get
+ {
+ return metaEvent;
+ }
+ }
+
+ ///
+ /// Empty constructor
+ ///
+ protected MetaEvent()
+ {
+ }
+
+ ///
+ /// Custom constructor for use by derived types, who will manage the data themselves
+ ///
+ /// Meta event type
+ /// Meta data length
+ /// Absolute time
+ public MetaEvent(MetaEventType metaEventType, int metaDataLength, long absoluteTime)
+ : base(absoluteTime,1,MidiCommandCode.MetaEvent)
+ {
+ this.metaEvent = metaEventType;
+ this.metaDataLength = metaDataLength;
+ }
+
+ ///
+ /// Reads a meta-event from a stream
+ ///
+ /// A binary reader based on the stream of MIDI data
+ /// A new MetaEvent object
+ public static MetaEvent ReadMetaEvent(BinaryReader br)
+ {
+ MetaEventType metaEvent = (MetaEventType) br.ReadByte();
+ int length = ReadVarInt(br);
+
+ MetaEvent me = new MetaEvent();
+ switch(metaEvent)
+ {
+ case MetaEventType.TrackSequenceNumber: // Sets the track's sequence number.
+ me = new TrackSequenceNumberEvent(br,length);
+ break;
+ case MetaEventType.TextEvent: // Text event
+ case MetaEventType.Copyright: // Copyright
+ case MetaEventType.SequenceTrackName: // Sequence / Track Name
+ case MetaEventType.TrackInstrumentName: // Track instrument name
+ case MetaEventType.Lyric: // lyric
+ case MetaEventType.Marker: // marker
+ case MetaEventType.CuePoint: // cue point
+ case MetaEventType.ProgramName:
+ case MetaEventType.DeviceName:
+ me = new TextEvent(br,length);
+ break;
+ case MetaEventType.EndTrack: // This event must come at the end of each track
+ if(length != 0)
+ {
+ throw new FormatException("End track length");
+ }
+ break;
+ case MetaEventType.SetTempo: // Set tempo
+ me = new TempoEvent(br,length);
+ break;
+ case MetaEventType.TimeSignature: // Time signature
+ me = new TimeSignatureEvent(br,length);
+ break;
+ case MetaEventType.KeySignature: // Key signature
+ me = new KeySignatureEvent(br, length);
+ break;
+ case MetaEventType.SequencerSpecific: // Sequencer specific information
+ me = new SequencerSpecificEvent(br, length);
+ break;
+ case MetaEventType.SmpteOffset:
+ me = new SmpteOffsetEvent(br, length);
+ break;
+ default:
+//System.Windows.Forms.MessageBox.Show(String.Format("Unsupported MetaEvent {0} length {1} pos {2}",metaEvent,length,br.BaseStream.Position));
+ me.data = br.ReadBytes(length);
+ if (me.data.Length != length)
+ {
+ throw new FormatException("Failed to read metaevent's data fully");
+ }
+ break;
+ }
+ me.metaEvent = metaEvent;
+ me.metaDataLength = length;
+
+ return me;
+ }
+
+ ///
+ /// Describes this Meta event
+ ///
+ /// String describing the metaevent
+ public override string ToString()
+ {
+ if (data == null)
+ {
+ return String.Format("{0} {1}", this.AbsoluteTime, metaEvent);
+ }
+ StringBuilder sb = new StringBuilder();
+ foreach (byte b in data)
+ {
+ sb.AppendFormat("{0:X2} ", b);
+ }
+ return String.Format("{0} {1}\r\n{2}", this.AbsoluteTime, metaEvent,sb.ToString());
+ }
+
+ ///
+ ///
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write((byte)metaEvent);
+ WriteVarInt(writer, metaDataLength);
+ if(data != null)
+ writer.Write(data,0,data.Length);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/MetaEventType.cs b/NAudio/Midi/MetaEventType.cs
new file mode 100644
index 00000000..83e626e1
--- /dev/null
+++ b/NAudio/Midi/MetaEventType.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace NAudio.Midi
+{
+ ///
+ /// MIDI MetaEvent Type
+ ///
+ public enum MetaEventType : byte
+ {
+ /// Track sequence number
+ TrackSequenceNumber = 0x00,
+ /// Text event
+ TextEvent = 0x01,
+ /// Copyright
+ Copyright = 0x02,
+ /// Sequence track name
+ SequenceTrackName = 0x03,
+ /// Track instrument name
+ TrackInstrumentName = 0x04,
+ /// Lyric
+ Lyric = 0x05,
+ /// Marker
+ Marker = 0x06,
+ /// Cue point
+ CuePoint = 0x07,
+ /// Program (patch) name
+ ProgramName = 0x08,
+ /// Device (port) name
+ DeviceName = 0x09,
+ /// MIDI Channel (not official?)
+ MidiChannel = 0x20,
+ /// MIDI Port (not official?)
+ MidiPort = 0x21,
+ /// End track
+ EndTrack = 0x2F,
+ /// Set tempo
+ SetTempo = 0x51,
+ /// SMPTE offset
+ SmpteOffset = 0x54,
+ /// Time signature
+ TimeSignature = 0x58,
+ /// Key signature
+ KeySignature = 0x59,
+ /// Sequencer specific
+ SequencerSpecific = 0x7F,
+ }
+}
diff --git a/NAudio/Midi/MidiCommandCode.cs b/NAudio/Midi/MidiCommandCode.cs
new file mode 100644
index 00000000..55ebc6e6
--- /dev/null
+++ b/NAudio/Midi/MidiCommandCode.cs
@@ -0,0 +1,39 @@
+namespace NAudio.Midi
+{
+ ///
+ /// MIDI command codes
+ ///
+ public enum MidiCommandCode : byte
+ {
+ /// Note Off
+ NoteOff = 0x80,
+ /// Note On
+ NoteOn = 0x90,
+ /// Key After-touch
+ KeyAfterTouch = 0xA0,
+ /// Control change
+ ControlChange = 0xB0,
+ /// Patch change
+ PatchChange = 0xC0,
+ /// Channel after-touch
+ ChannelAfterTouch = 0xD0,
+ /// Pitch wheel change
+ PitchWheelChange = 0xE0,
+ /// Sysex message
+ Sysex = 0xF0,
+ /// Eox (comes at end of a sysex message)
+ Eox = 0xF7,
+ /// Timing clock (used when synchronization is required)
+ TimingClock = 0xF8,
+ /// Start sequence
+ StartSequence = 0xFA,
+ /// Continue sequence
+ ContinueSequence = 0xFB,
+ /// Stop sequence
+ StopSequence = 0xFC,
+ /// Auto-Sensing
+ AutoSensing = 0xFE,
+ /// Meta-event
+ MetaEvent = 0xFF,
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/MidiController.cs b/NAudio/Midi/MidiController.cs
new file mode 100644
index 00000000..c21eb3ac
--- /dev/null
+++ b/NAudio/Midi/MidiController.cs
@@ -0,0 +1,42 @@
+using System;
+
+namespace NAudio.Midi
+{
+ ///
+ /// MidiController enumeration
+ /// http://www.midi.org/techspecs/midimessages.php#3
+ ///
+ public enum MidiController : byte
+ {
+ /// Bank Select (MSB)
+ BankSelect = 0,
+ /// Modulation (MSB)
+ Modulation = 1,
+ /// Breath Controller
+ BreathController = 2,
+ /// Foot controller (MSB)
+ FootController = 4,
+ /// Main volume
+ MainVolume = 7,
+ /// Pan
+ Pan = 10,
+ /// Expression
+ Expression = 11,
+ /// Bank Select LSB
+ BankSelectLsb = 32,
+ /// Sustain
+ Sustain = 64,
+ /// Portamento On/Off
+ Portamento = 65,
+ /// Sostenuto On/Off
+ Sostenuto = 66,
+ /// Soft Pedal On/Off
+ SoftPedal = 67,
+ /// Legato Footswitch
+ LegatoFootswitch = 68,
+ /// Reset all controllers
+ ResetAllControllers = 121,
+ /// All notes off
+ AllNotesOff = 123,
+ }
+}
diff --git a/NAudio/Midi/MidiEvent.cs b/NAudio/Midi/MidiEvent.cs
new file mode 100644
index 00000000..7b2b1d69
--- /dev/null
+++ b/NAudio/Midi/MidiEvent.cs
@@ -0,0 +1,392 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents an individual MIDI event
+ ///
+ public class MidiEvent
+ {
+ /// The MIDI command code
+ private MidiCommandCode commandCode;
+ private int channel;
+ private int deltaTime;
+ private long absoluteTime;
+
+ ///
+ /// Creates a MidiEvent from a raw message received using
+ /// the MME MIDI In APIs
+ ///
+ /// The short MIDI message
+ /// A new MIDI Event
+ public static MidiEvent FromRawMessage(int rawMessage)
+ {
+ long absoluteTime = 0;
+ int b = rawMessage & 0xFF;
+ int data1 = (rawMessage >> 8) & 0xFF;
+ int data2 = (rawMessage >> 16) & 0xFF;
+ MidiCommandCode commandCode;
+ int channel = 1;
+
+ if ((b & 0xF0) == 0xF0)
+ {
+ // both bytes are used for command code in this case
+ commandCode = (MidiCommandCode)b;
+ }
+ else
+ {
+ commandCode = (MidiCommandCode)(b & 0xF0);
+ channel = (b & 0x0F) + 1;
+ }
+
+ MidiEvent me;
+ switch (commandCode)
+ {
+ case MidiCommandCode.NoteOn:
+ case MidiCommandCode.NoteOff:
+ case MidiCommandCode.KeyAfterTouch:
+ if (data2 > 0 && commandCode == MidiCommandCode.NoteOn)
+ {
+ me = new NoteOnEvent(absoluteTime, channel, data1, data2, 0);
+ }
+ else
+ {
+ me = new NoteEvent(absoluteTime, channel, commandCode, data1, data2);
+ }
+ break;
+ case MidiCommandCode.ControlChange:
+ me = new ControlChangeEvent(absoluteTime,channel,(MidiController)data1,data2);
+ break;
+ case MidiCommandCode.PatchChange:
+ me = new PatchChangeEvent(absoluteTime,channel,data1);
+ break;
+ case MidiCommandCode.ChannelAfterTouch:
+ me = new ChannelAfterTouchEvent(absoluteTime,channel,data1);
+ break;
+ case MidiCommandCode.PitchWheelChange:
+ me = new PitchWheelChangeEvent(absoluteTime, channel, data1 + (data2 << 7));
+ break;
+ case MidiCommandCode.TimingClock:
+ case MidiCommandCode.StartSequence:
+ case MidiCommandCode.ContinueSequence:
+ case MidiCommandCode.StopSequence:
+ case MidiCommandCode.AutoSensing:
+ me = new MidiEvent(absoluteTime,channel,commandCode);
+ break;
+ case MidiCommandCode.MetaEvent:
+ case MidiCommandCode.Sysex:
+ default:
+ throw new FormatException(String.Format("Unsupported MIDI Command Code for Raw Message {0}", commandCode));
+ }
+ return me;
+
+ }
+
+ ///
+ /// Constructs a MidiEvent from a BinaryStream
+ ///
+ /// The binary stream of MIDI data
+ /// The previous MIDI event (pass null for first event)
+ /// A new MidiEvent
+ public static MidiEvent ReadNextEvent(BinaryReader br, MidiEvent previous)
+ {
+ int deltaTime = MidiEvent.ReadVarInt(br);
+ MidiCommandCode commandCode;
+ int channel = 1;
+ byte b = br.ReadByte();
+ if((b & 0x80) == 0)
+ {
+ // a running command - command & channel are same as previous
+ commandCode = previous.CommandCode;
+ channel = previous.Channel;
+ br.BaseStream.Position--; // need to push this back
+ }
+ else
+ {
+ if((b & 0xF0) == 0xF0)
+ {
+ // both bytes are used for command code in this case
+ commandCode = (MidiCommandCode) b;
+ }
+ else
+ {
+ commandCode = (MidiCommandCode) (b & 0xF0);
+ channel = (b & 0x0F) + 1;
+ }
+ }
+
+ MidiEvent me;
+ switch(commandCode)
+ {
+ case MidiCommandCode.NoteOn:
+ me = new NoteOnEvent(br);
+ break;
+ case MidiCommandCode.NoteOff:
+ case MidiCommandCode.KeyAfterTouch:
+ me = new NoteEvent(br);
+ break;
+ case MidiCommandCode.ControlChange:
+ me = new ControlChangeEvent(br);
+ break;
+ case MidiCommandCode.PatchChange:
+ me = new PatchChangeEvent(br);
+ break;
+ case MidiCommandCode.ChannelAfterTouch:
+ me = new ChannelAfterTouchEvent(br);
+ break;
+ case MidiCommandCode.PitchWheelChange:
+ me = new PitchWheelChangeEvent(br);
+ break;
+ case MidiCommandCode.TimingClock:
+ case MidiCommandCode.StartSequence:
+ case MidiCommandCode.ContinueSequence:
+ case MidiCommandCode.StopSequence:
+ me = new MidiEvent();
+ break;
+ case MidiCommandCode.Sysex:
+ me = SysexEvent.ReadSysexEvent(br);
+ break;
+ case MidiCommandCode.MetaEvent:
+ me = MetaEvent.ReadMetaEvent(br);
+ break;
+ default:
+ throw new FormatException(String.Format("Unsupported MIDI Command Code {0:X2}",(byte) commandCode));
+ }
+ me.channel = channel;
+ me.deltaTime = deltaTime;
+ me.commandCode = commandCode;
+ return me;
+ }
+
+ ///
+ /// Converts this MIDI event to a short message (32 bit integer) that
+ /// can be sent by the Windows MIDI out short message APIs
+ /// Cannot be implemented for all MIDI messages
+ ///
+ /// A short message
+ public virtual int GetAsShortMessage()
+ {
+ return (channel - 1) + (int)commandCode;
+ }
+
+ ///
+ /// Default constructor
+ ///
+ protected MidiEvent()
+ {
+ }
+
+ ///
+ /// Creates a MIDI event with specified parameters
+ ///
+ /// Absolute time of this event
+ /// MIDI channel number
+ /// MIDI command code
+ public MidiEvent(long absoluteTime, int channel, MidiCommandCode commandCode)
+ {
+ this.absoluteTime = absoluteTime;
+ this.Channel = channel;
+ this.commandCode = commandCode;
+ }
+
+ ///
+ /// The MIDI Channel Number for this event (1-16)
+ ///
+ public virtual int Channel
+ {
+ get
+ {
+ return channel;
+ }
+ set
+ {
+ if ((value < 1) || (value > 16))
+ {
+ throw new ArgumentOutOfRangeException("value", value,
+ String.Format("Channel must be 1-16 (Got {0})",value));
+ }
+ channel = value;
+ }
+ }
+
+ ///
+ /// The Delta time for this event
+ ///
+ public int DeltaTime
+ {
+ get
+ {
+ return deltaTime;
+ }
+ }
+
+ ///
+ /// The absolute time for this event
+ ///
+ public long AbsoluteTime
+ {
+ get
+ {
+ return absoluteTime;
+ }
+ set
+ {
+ absoluteTime = value;
+ }
+ }
+
+ ///
+ /// The command code for this event
+ ///
+ public MidiCommandCode CommandCode
+ {
+ get
+ {
+ return commandCode;
+ }
+ }
+
+ ///
+ /// Whether this is a note off event
+ ///
+ public static bool IsNoteOff(MidiEvent midiEvent)
+ {
+ if (midiEvent != null)
+ {
+ if (midiEvent.CommandCode == MidiCommandCode.NoteOn)
+ {
+ NoteEvent ne = (NoteEvent)midiEvent;
+ return (ne.Velocity == 0);
+ }
+ return (midiEvent.CommandCode == MidiCommandCode.NoteOff);
+ }
+ return false;
+ }
+
+ ///
+ /// Whether this is a note on event
+ ///
+ public static bool IsNoteOn(MidiEvent midiEvent)
+ {
+ if (midiEvent != null)
+ {
+ if (midiEvent.CommandCode == MidiCommandCode.NoteOn)
+ {
+ NoteEvent ne = (NoteEvent)midiEvent;
+ return (ne.Velocity > 0);
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Determines if this is an end track event
+ ///
+ public static bool IsEndTrack(MidiEvent midiEvent)
+ {
+ if (midiEvent != null)
+ {
+ MetaEvent me = midiEvent as MetaEvent;
+ if (me != null)
+ {
+ return me.MetaEventType == MetaEventType.EndTrack;
+ }
+ }
+ return false;
+ }
+
+
+ ///
+ /// Displays a summary of the MIDI event
+ ///
+ /// A string containing a brief description of this MIDI event
+ public override string ToString()
+ {
+ if(commandCode >= MidiCommandCode.Sysex)
+ return String.Format("{0} {1}",absoluteTime,commandCode);
+ else
+ return String.Format("{0} {1} Ch: {2}", absoluteTime, commandCode, channel);
+ }
+
+ ///
+ /// Utility function that can read a variable length integer from a binary stream
+ ///
+ /// The binary stream
+ /// The integer read
+ public static int ReadVarInt(BinaryReader br)
+ {
+ int value = 0;
+ byte b;
+ for(int n = 0; n < 4; n++)
+ {
+ b = br.ReadByte();
+ value <<= 7;
+ value += (b & 0x7F);
+ if((b & 0x80) == 0)
+ {
+ return value;
+ }
+ }
+ throw new FormatException("Invalid Var Int");
+ }
+
+ ///
+ /// Writes a variable length integer to a binary stream
+ ///
+ /// Binary stream
+ /// The value to write
+ public static void WriteVarInt(BinaryWriter writer, int value)
+ {
+ if (value < 0)
+ {
+ throw new ArgumentOutOfRangeException("value", value, "Cannot write a negative Var Int");
+ }
+ if (value > 0x0FFFFFFF)
+ {
+ throw new ArgumentOutOfRangeException("value", value, "Maximum allowed Var Int is 0x0FFFFFFF");
+ }
+
+ int n = 0;
+ byte[] buffer = new byte[4];
+ do
+ {
+ buffer[n++] = (byte)(value & 0x7F);
+ value >>= 7;
+ } while (value > 0);
+
+ while (n > 0)
+ {
+ n--;
+ if(n > 0)
+ writer.Write((byte) (buffer[n] | 0x80));
+ else
+ writer.Write(buffer[n]);
+ }
+ }
+
+ ///
+ /// Exports this MIDI event's data
+ /// Overriden in derived classes, but they should call this version
+ ///
+ /// Absolute time used to calculate delta.
+ /// Is updated ready for the next delta calculation
+ /// Stream to write to
+ public virtual void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ if (this.absoluteTime < absoluteTime)
+ {
+ throw new FormatException("Can't export unsorted MIDI events");
+ }
+ WriteVarInt(writer,(int) (this.absoluteTime - absoluteTime));
+ absoluteTime = this.absoluteTime;
+ int output = (int) commandCode;
+ if (commandCode != MidiCommandCode.MetaEvent)
+ {
+ output += (channel - 1);
+ }
+ writer.Write((byte)output);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/MidiEventCollection.cs b/NAudio/Midi/MidiEventCollection.cs
new file mode 100644
index 00000000..35465014
--- /dev/null
+++ b/NAudio/Midi/MidiEventCollection.cs
@@ -0,0 +1,322 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Midi
+{
+ ///
+ /// A helper class to manage collection of MIDI events
+ /// It has the ability to organise them in tracks
+ ///
+ public class MidiEventCollection : IEnumerable>
+ {
+ int midiFileType;
+ List> trackEvents;
+ int deltaTicksPerQuarterNote;
+ long startAbsoluteTime;
+
+ ///
+ /// Creates a new Midi Event collection
+ ///
+ /// Initial file type
+ /// Delta Ticks Per Quarter Note
+ public MidiEventCollection(int midiFileType, int deltaTicksPerQuarterNote)
+ {
+ this.midiFileType = midiFileType;
+ this.deltaTicksPerQuarterNote = deltaTicksPerQuarterNote;
+ this.startAbsoluteTime = 0;
+ trackEvents = new List>();
+ }
+
+ ///
+ /// The number of tracks
+ ///
+ public int Tracks
+ {
+ get
+ {
+ return trackEvents.Count;
+ }
+ }
+
+ ///
+ /// The absolute time that should be considered as time zero
+ /// Not directly used here, but useful for timeshifting applications
+ ///
+ public long StartAbsoluteTime
+ {
+ get
+ {
+ return startAbsoluteTime;
+ }
+ set
+ {
+ startAbsoluteTime = value;
+ }
+ }
+
+ ///
+ /// The number of ticks per quarter note
+ ///
+ public int DeltaTicksPerQuarterNote
+ {
+ get { return deltaTicksPerQuarterNote; }
+ }
+
+ ///
+ /// Gets events on a specified track
+ ///
+ /// Track number
+ /// The list of events
+ public IList GetTrackEvents(int trackNumber)
+ {
+ return trackEvents[trackNumber];
+ }
+
+ ///
+ /// Gets events on a specific track
+ ///
+ /// Track number
+ /// The list of events
+ public IList this[int trackNumber]
+ {
+ get { return trackEvents[trackNumber]; }
+ }
+
+ ///
+ /// Adds a new track
+ ///
+ /// The new track event list
+ public IList AddTrack()
+ {
+ return AddTrack(null);
+ }
+
+ ///
+ /// Adds a new track
+ ///
+ /// Initial events to add to the new track
+ /// The new track event list
+ public IList AddTrack(IList initialEvents)
+ {
+ List events = new List();
+ if (initialEvents != null)
+ {
+ events.AddRange(initialEvents);
+ }
+ trackEvents.Add(events);
+ return events;
+ }
+
+ ///
+ /// Removes a track
+ ///
+ /// Track number to remove
+ public void RemoveTrack(int track)
+ {
+ trackEvents.RemoveAt(track);
+ }
+
+ ///
+ /// Clears all events
+ ///
+ public void Clear()
+ {
+ trackEvents.Clear();
+ }
+
+ ///
+ /// The MIDI file type
+ ///
+ public int MidiFileType
+ {
+ get
+ {
+ return midiFileType;
+ }
+ set
+ {
+ if (midiFileType != value)
+ {
+ // set MIDI file type before calling flatten or explode functions
+ midiFileType = value;
+
+ if (value == 0)
+ {
+ FlattenToOneTrack();
+ }
+ else
+ {
+ ExplodeToManyTracks();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Adds an event to the appropriate track depending on file type
+ ///
+ /// The event to be added
+ /// The original (or desired) track number
+ /// When adding events in type 0 mode, the originalTrack parameter
+ /// is ignored. If in type 1 mode, it will use the original track number to
+ /// store the new events. If the original track was 0 and this is a channel based
+ /// event, it will create new tracks if necessary and put it on the track corresponding
+ /// to its channel number
+ public void AddEvent(MidiEvent midiEvent, int originalTrack)
+ {
+ if (midiFileType == 0)
+ {
+ EnsureTracks(1);
+ trackEvents[0].Add(midiEvent);
+ }
+ else
+ {
+ if(originalTrack == 0)
+ {
+ // if its a channel based event, lets move it off to
+ // a channel track of its own
+ switch (midiEvent.CommandCode)
+ {
+ case MidiCommandCode.NoteOff:
+ case MidiCommandCode.NoteOn:
+ case MidiCommandCode.KeyAfterTouch:
+ case MidiCommandCode.ControlChange:
+ case MidiCommandCode.PatchChange:
+ case MidiCommandCode.ChannelAfterTouch:
+ case MidiCommandCode.PitchWheelChange:
+ EnsureTracks(midiEvent.Channel + 1);
+ trackEvents[midiEvent.Channel].Add(midiEvent);
+ break;
+ default:
+ EnsureTracks(1);
+ trackEvents[0].Add(midiEvent);
+ break;
+ }
+
+ }
+ else
+ {
+ // put it on the track it was originally on
+ EnsureTracks(originalTrack + 1);
+ trackEvents[originalTrack].Add(midiEvent);
+ }
+ }
+ }
+
+
+ private void EnsureTracks(int count)
+ {
+ for (int n = trackEvents.Count; n < count; n++)
+ {
+ trackEvents.Add(new List());
+ }
+ }
+
+ private void ExplodeToManyTracks()
+ {
+ IList originalList = trackEvents[0];
+ Clear();
+ foreach (MidiEvent midiEvent in originalList)
+ {
+ AddEvent(midiEvent, 0);
+ }
+ PrepareForExport();
+ }
+
+ private void FlattenToOneTrack()
+ {
+ bool eventsAdded = false;
+ for (int track = 1; track < trackEvents.Count; track++)
+ {
+ foreach (MidiEvent midiEvent in trackEvents[track])
+ {
+ if (!MidiEvent.IsEndTrack(midiEvent))
+ {
+ trackEvents[0].Add(midiEvent);
+ eventsAdded = true;
+ }
+ }
+ }
+ for (int track = trackEvents.Count - 1; track > 0; track--)
+ {
+ RemoveTrack(track);
+ }
+ if (eventsAdded)
+ {
+ PrepareForExport();
+ }
+ }
+
+ ///
+ /// Sorts, removes empty tracks and adds end track markers
+ ///
+ public void PrepareForExport()
+ {
+ var comparer = new MidiEventComparer();
+ // 1. sort each track
+ foreach (List list in trackEvents)
+ {
+ MergeSort.Sort(list, comparer);
+
+ // 2. remove all End track events except one at the very end
+ int index = 0;
+ while (index < list.Count - 1)
+ {
+ if(MidiEvent.IsEndTrack(list[index]))
+ {
+ list.RemoveAt(index);
+ }
+ else
+ {
+ index++;
+ }
+ }
+ }
+
+ int track = 0;
+ // 3. remove empty tracks and add missing
+ while (track < trackEvents.Count)
+ {
+ IList list = trackEvents[track];
+ if (list.Count == 0)
+ {
+ RemoveTrack(track);
+ }
+ else
+ {
+ if(list.Count == 1 && MidiEvent.IsEndTrack(list[0]))
+ {
+ RemoveTrack(track);
+ }
+ else
+ {
+ if(!MidiEvent.IsEndTrack(list[list.Count-1]))
+ {
+ list.Add(new MetaEvent(MetaEventType.EndTrack, 0, list[list.Count - 1].AbsoluteTime));
+ }
+ track++;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets an enumerator for the lists of track events
+ ///
+ public IEnumerator> GetEnumerator()
+ {
+ return trackEvents.GetEnumerator();
+
+ }
+
+ ///
+ /// Gets an enumerator for the lists of track events
+ ///
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+ {
+ return trackEvents.GetEnumerator();
+ }
+ }
+}
diff --git a/NAudio/Midi/MidiEventComparer.cs b/NAudio/Midi/MidiEventComparer.cs
new file mode 100644
index 00000000..15c91367
--- /dev/null
+++ b/NAudio/Midi/MidiEventComparer.cs
@@ -0,0 +1,50 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Utility class for comparing MidiEvent objects
+ ///
+ public class MidiEventComparer : IComparer
+ {
+ #region IComparer Members
+
+ ///
+ /// Compares two MidiEvents
+ /// Sorts by time, with EndTrack always sorted to the end
+ ///
+ public int Compare(MidiEvent x, MidiEvent y)
+ {
+ long xTime = x.AbsoluteTime;
+ long yTime = y.AbsoluteTime;
+
+ if (xTime == yTime)
+ {
+ // sort meta events before note events, except end track
+ MetaEvent xMeta = x as MetaEvent;
+ MetaEvent yMeta = y as MetaEvent;
+
+ if (xMeta != null)
+ {
+ if (xMeta.MetaEventType == MetaEventType.EndTrack)
+ xTime = Int64.MaxValue;
+ else
+ xTime = Int64.MinValue;
+ }
+ if (yMeta != null)
+ {
+ if (yMeta.MetaEventType == MetaEventType.EndTrack)
+ yTime = Int64.MaxValue;
+ else
+ yTime = Int64.MinValue;
+ }
+ }
+ return xTime.CompareTo(yTime);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/MidiFile.cs b/NAudio/Midi/MidiFile.cs
new file mode 100644
index 00000000..fa9795dd
--- /dev/null
+++ b/NAudio/Midi/MidiFile.cs
@@ -0,0 +1,270 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using NAudio.Utils;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Class able to read a MIDI file
+ ///
+ public class MidiFile
+ {
+ private MidiEventCollection events;
+ private ushort fileFormat;
+ //private ushort tracks;
+ private ushort deltaTicksPerQuarterNote;
+ private bool strictChecking;
+
+ ///
+ /// Opens a MIDI file for reading
+ ///
+ /// Name of MIDI file
+ public MidiFile(string filename)
+ : this(filename,true)
+ {
+ }
+
+ ///
+ /// MIDI File format
+ ///
+ public int FileFormat
+ {
+ get { return fileFormat; }
+ }
+
+ ///
+ /// Opens a MIDI file for reading
+ ///
+ /// Name of MIDI file
+ /// If true will error on non-paired note events
+ public MidiFile(string filename, bool strictChecking)
+ {
+ this.strictChecking = strictChecking;
+
+ var br = new BinaryReader(File.OpenRead(filename));
+ using(br)
+ {
+ string chunkHeader = Encoding.UTF8.GetString(br.ReadBytes(4));
+ if(chunkHeader != "MThd")
+ {
+ throw new FormatException("Not a MIDI file - header chunk missing");
+ }
+ uint chunkSize = SwapUInt32(br.ReadUInt32());
+
+ if(chunkSize != 6)
+ {
+ throw new FormatException("Unexpected header chunk length");
+ }
+ // 0 = single track, 1 = multi-track synchronous, 2 = multi-track asynchronous
+ fileFormat = SwapUInt16(br.ReadUInt16());
+ int tracks = SwapUInt16(br.ReadUInt16());
+ deltaTicksPerQuarterNote = SwapUInt16(br.ReadUInt16());
+
+ events = new MidiEventCollection((fileFormat == 0) ? 0 : 1, deltaTicksPerQuarterNote);
+ for (int n = 0; n < tracks; n++)
+ {
+ events.AddTrack();
+ }
+
+ long absoluteTime = 0;
+
+ for(int track = 0; track < tracks; track++)
+ {
+ if(fileFormat == 1)
+ {
+ absoluteTime = 0;
+ }
+ chunkHeader = Encoding.UTF8.GetString(br.ReadBytes(4));
+ if(chunkHeader != "MTrk")
+ {
+ throw new FormatException("Invalid chunk header");
+ }
+ chunkSize = SwapUInt32(br.ReadUInt32());
+
+ long startPos = br.BaseStream.Position;
+ MidiEvent me = null;
+ var outstandingNoteOns = new List();
+ while(br.BaseStream.Position < startPos + chunkSize)
+ {
+ me = MidiEvent.ReadNextEvent(br,me);
+ absoluteTime += me.DeltaTime;
+ me.AbsoluteTime = absoluteTime;
+ events[track].Add(me);
+ if (me.CommandCode == MidiCommandCode.NoteOn)
+ {
+ NoteEvent ne = (NoteEvent) me;
+ if(ne.Velocity > 0)
+ {
+ outstandingNoteOns.Add((NoteOnEvent) ne);
+ }
+ else
+ {
+ // don't remove the note offs, even though
+ // they are annoying
+ // events[track].Remove(me);
+ FindNoteOn(ne,outstandingNoteOns);
+ }
+ }
+ else if(me.CommandCode == MidiCommandCode.NoteOff)
+ {
+ FindNoteOn((NoteEvent) me,outstandingNoteOns);
+ }
+ else if(me.CommandCode == MidiCommandCode.MetaEvent)
+ {
+ MetaEvent metaEvent = (MetaEvent) me;
+ if(metaEvent.MetaEventType == MetaEventType.EndTrack)
+ {
+ //break;
+ // some dodgy MIDI files have an event after end track
+ if (strictChecking)
+ {
+ if (br.BaseStream.Position < startPos + chunkSize)
+ {
+ throw new FormatException(String.Format("End Track event was not the last MIDI event on track {0}", track));
+ }
+ }
+ }
+ }
+ }
+ if(outstandingNoteOns.Count > 0)
+ {
+ if (strictChecking)
+ {
+ throw new FormatException(String.Format("Note ons without note offs {0} (file format {1})", outstandingNoteOns.Count, fileFormat));
+ }
+ }
+ if(br.BaseStream.Position != startPos + chunkSize)
+ {
+ throw new FormatException(String.Format("Read too far {0}+{1}!={2}", chunkSize, startPos, br.BaseStream.Position));
+ }
+ }
+ }
+ }
+
+ ///
+ /// The collection of events in this MIDI file
+ ///
+ public MidiEventCollection Events
+ {
+ get { return events; }
+ }
+
+ ///
+ /// Number of tracks in this MIDI file
+ ///
+ public int Tracks
+ {
+ get { return events.Tracks; }
+ }
+
+ ///
+ /// Delta Ticks Per Quarter Note
+ ///
+ public int DeltaTicksPerQuarterNote
+ {
+ get { return deltaTicksPerQuarterNote; }
+ }
+
+ private void FindNoteOn(NoteEvent offEvent, List outstandingNoteOns)
+ {
+ bool found = false;
+ foreach(NoteOnEvent noteOnEvent in outstandingNoteOns)
+ {
+ if ((noteOnEvent.Channel == offEvent.Channel) && (noteOnEvent.NoteNumber == offEvent.NoteNumber))
+ {
+ noteOnEvent.OffEvent = offEvent;
+ outstandingNoteOns.Remove(noteOnEvent);
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ {
+ if (strictChecking)
+ {
+ throw new FormatException(String.Format("Got an off without an on {0}", offEvent));
+ }
+ }
+ }
+
+ private static uint SwapUInt32(uint i)
+ {
+ return ((i & 0xFF000000) >> 24) | ((i & 0x00FF0000) >> 8) | ((i & 0x0000FF00) << 8) | ((i & 0x000000FF) << 24);
+ }
+
+ private static ushort SwapUInt16(ushort i)
+ {
+ return (ushort) (((i & 0xFF00) >> 8) | ((i & 0x00FF) << 8));
+ }
+
+ ///
+ /// Describes the MIDI file
+ ///
+ /// A string describing the MIDI file and its events
+ public override string ToString()
+ {
+ var sb = new StringBuilder();
+ sb.AppendFormat("Format {0}, Tracks {1}, Delta Ticks Per Quarter Note {2}\r\n",
+ fileFormat,Tracks,deltaTicksPerQuarterNote);
+ for (int n = 0; n < Tracks; n++)
+ {
+ foreach (MidiEvent midiEvent in events[n])
+ {
+ sb.AppendFormat("{0}\r\n", midiEvent);
+ }
+ }
+ return sb.ToString();
+ }
+
+ ///
+ /// Exports a MIDI file
+ ///
+ /// Filename to export to
+ /// Events to export
+ public static void Export(string filename, MidiEventCollection events)
+ {
+ if (events.MidiFileType == 0 && events.Tracks > 1)
+ {
+ throw new ArgumentException("Can't export more than one track to a type 0 file");
+ }
+ using (var writer = new BinaryWriter(File.Create(filename)))
+ {
+ writer.Write(Encoding.UTF8.GetBytes("MThd"));
+ writer.Write(SwapUInt32((uint)6)); // chunk size
+ writer.Write(SwapUInt16((ushort)events.MidiFileType));
+ writer.Write(SwapUInt16((ushort)events.Tracks));
+ writer.Write(SwapUInt16((ushort)events.DeltaTicksPerQuarterNote));
+
+ for (int track = 0; track < events.Tracks; track++ )
+ {
+ IList eventList = events[track];
+
+ writer.Write(Encoding.UTF8.GetBytes("MTrk"));
+ long trackSizePosition = writer.BaseStream.Position;
+ writer.Write(SwapUInt32((uint)0));
+
+ long absoluteTime = events.StartAbsoluteTime;
+
+ // use a stable sort to preserve ordering of MIDI events whose
+ // absolute times are the same
+ MergeSort.Sort(eventList, new MidiEventComparer());
+ if (eventList.Count > 0)
+ {
+ System.Diagnostics.Debug.Assert(MidiEvent.IsEndTrack(eventList[eventList.Count - 1]), "Exporting a track with a missing end track");
+ }
+ foreach (MidiEvent midiEvent in eventList)
+ {
+ midiEvent.Export(ref absoluteTime, writer);
+ }
+
+ uint trackChunkLength = (uint)(writer.BaseStream.Position - trackSizePosition) - 4;
+ writer.BaseStream.Position = trackSizePosition;
+ writer.Write(SwapUInt32(trackChunkLength));
+ writer.BaseStream.Position += trackChunkLength;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/MidiIn.cs b/NAudio/Midi/MidiIn.cs
new file mode 100644
index 00000000..15cde2ca
--- /dev/null
+++ b/NAudio/Midi/MidiIn.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI in device
+ ///
+ public class MidiIn : IDisposable
+ {
+ private IntPtr hMidiIn = IntPtr.Zero;
+ private bool disposed = false;
+ private MidiInterop.MidiInCallback callback;
+
+ ///
+ /// Called when a MIDI message is received
+ ///
+ public event EventHandler MessageReceived;
+
+ ///
+ /// An invalid MIDI message
+ ///
+ public event EventHandler ErrorReceived;
+
+ ///
+ /// Gets the number of MIDI input devices available in the system
+ ///
+ public static int NumberOfDevices
+ {
+ get
+ {
+ return MidiInterop.midiInGetNumDevs();
+ }
+ }
+
+ ///
+ /// Opens a specified MIDI in device
+ ///
+ /// The device number
+ public MidiIn(int deviceNo)
+ {
+ this.callback = new MidiInterop.MidiInCallback(Callback);
+ MmException.Try(MidiInterop.midiInOpen(out hMidiIn, (IntPtr) deviceNo,this.callback,IntPtr.Zero,MidiInterop.CALLBACK_FUNCTION),"midiInOpen");
+ }
+
+ ///
+ /// Closes this MIDI in device
+ ///
+ public void Close()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Closes this MIDI in device
+ ///
+ public void Dispose()
+ {
+ GC.KeepAlive(callback);
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Start the MIDI in device
+ ///
+ public void Start()
+ {
+ MmException.Try(MidiInterop.midiInStart(hMidiIn), "midiInStart");
+ }
+
+ ///
+ /// Stop the MIDI in device
+ ///
+ public void Stop()
+ {
+ MmException.Try(MidiInterop.midiInStop(hMidiIn), "midiInStop");
+ }
+
+ ///
+ /// Reset the MIDI in device
+ ///
+ public void Reset()
+ {
+ MmException.Try(MidiInterop.midiInReset(hMidiIn), "midiInReset");
+ }
+
+ private void Callback(IntPtr midiInHandle, MidiInterop.MidiInMessage message, IntPtr userData, IntPtr messageParameter1, IntPtr messageParameter2)
+ {
+ switch(message)
+ {
+ case MidiInterop.MidiInMessage.Open:
+ // message Parameter 1 & 2 are not used
+ break;
+ case MidiInterop.MidiInMessage.Data:
+ // parameter 1 is packed MIDI message
+ // parameter 2 is milliseconds since MidiInStart
+ if (MessageReceived != null)
+ {
+ MessageReceived(this, new MidiInMessageEventArgs(messageParameter1.ToInt32(), messageParameter2.ToInt32()));
+ }
+ break;
+ case MidiInterop.MidiInMessage.Error:
+ // parameter 1 is invalid MIDI message
+ if (ErrorReceived != null)
+ {
+ ErrorReceived(this, new MidiInMessageEventArgs(messageParameter1.ToInt32(), messageParameter2.ToInt32()));
+ }
+ break;
+ case MidiInterop.MidiInMessage.Close:
+ // message Parameter 1 & 2 are not used
+ break;
+ case MidiInterop.MidiInMessage.LongData:
+ // parameter 1 is pointer to MIDI header
+ // parameter 2 is milliseconds since MidiInStart
+ break;
+ case MidiInterop.MidiInMessage.LongError:
+ // parameter 1 is pointer to MIDI header
+ // parameter 2 is milliseconds since MidiInStart
+ break;
+ case MidiInterop.MidiInMessage.MoreData:
+ // parameter 1 is packed MIDI message
+ // parameter 2 is milliseconds since MidiInStart
+ break;
+ }
+ }
+
+ ///
+ /// Gets the MIDI in device info
+ ///
+ public static MidiInCapabilities DeviceInfo(int midiInDeviceNumber)
+ {
+ MidiInCapabilities caps = new MidiInCapabilities();
+ int structSize = Marshal.SizeOf(caps);
+ MmException.Try(MidiInterop.midiInGetDevCaps((IntPtr)midiInDeviceNumber,out caps,structSize),"midiInGetDevCaps");
+ return caps;
+ }
+
+ ///
+ /// Closes the MIDI out device
+ ///
+ /// True if called from Dispose
+ protected virtual void Dispose(bool disposing)
+ {
+ if(!this.disposed)
+ {
+ //if(disposing) Components.Dispose();
+ MidiInterop.midiInClose(hMidiIn);
+ }
+ disposed = true;
+ }
+
+ ///
+ /// Cleanup
+ ///
+ ~MidiIn()
+ {
+ System.Diagnostics.Debug.Assert(false,"MIDI In was not finalised");
+ Dispose(false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/MidiInCapabilities.cs b/NAudio/Midi/MidiInCapabilities.cs
new file mode 100644
index 00000000..1663e9e6
--- /dev/null
+++ b/NAudio/Midi/MidiInCapabilities.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Midi
+{
+ ///
+ /// MIDI In Device Capabilities
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public struct MidiInCapabilities
+ {
+ ///
+ /// wMid
+ ///
+ UInt16 manufacturerId;
+ ///
+ /// wPid
+ ///
+ UInt16 productId;
+ ///
+ /// vDriverVersion
+ ///
+ UInt32 driverVersion;
+ ///
+ /// Product Name
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxProductNameLength)]
+ string productName;
+ ///
+ /// Support - Reserved
+ ///
+ Int32 support;
+
+ private const int MaxProductNameLength = 32;
+
+ ///
+ /// Gets the manufacturer of this device
+ ///
+ public Manufacturers Manufacturer
+ {
+ get
+ {
+ return (Manufacturers)manufacturerId;
+ }
+ }
+
+ ///
+ /// Gets the product identifier (manufacturer specific)
+ ///
+ public int ProductId
+ {
+ get
+ {
+ return productId;
+ }
+ }
+
+ ///
+ /// Gets the product name
+ ///
+ public string ProductName
+ {
+ get
+ {
+ return productName;
+ }
+ }
+ }
+}
diff --git a/NAudio/Midi/MidiInMessageEventArgs.cs b/NAudio/Midi/MidiInMessageEventArgs.cs
new file mode 100644
index 00000000..3cdc50b1
--- /dev/null
+++ b/NAudio/Midi/MidiInMessageEventArgs.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace NAudio.Midi
+{
+ ///
+ /// MIDI In Message Information
+ ///
+ public class MidiInMessageEventArgs : EventArgs
+ {
+ ///
+ /// Create a new MIDI In Message EventArgs
+ ///
+ ///
+ ///
+ public MidiInMessageEventArgs(int message, int timestamp)
+ {
+ this.RawMessage = message;
+ this.Timestamp = timestamp;
+ try
+ {
+ this.MidiEvent = MidiEvent.FromRawMessage(message);
+ }
+ catch (Exception)
+ {
+ // don't worry too much - might be an invalid message
+ }
+ }
+
+ ///
+ /// The Raw message received from the MIDI In API
+ ///
+ public int RawMessage { get; private set; }
+
+ ///
+ /// The raw message interpreted as a MidiEvent
+ ///
+ public MidiEvent MidiEvent { get; private set; }
+
+ ///
+ /// The timestamp in milliseconds for this message
+ ///
+ public int Timestamp { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/MidiInterop.cs b/NAudio/Midi/MidiInterop.cs
new file mode 100644
index 00000000..ffac8c09
--- /dev/null
+++ b/NAudio/Midi/MidiInterop.cs
@@ -0,0 +1,277 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.Midi
+{
+ internal class MidiInterop
+ {
+
+ public enum MidiInMessage
+ {
+ ///
+ /// MIM_OPEN
+ ///
+ Open = 0x3C1,
+ ///
+ /// MIM_CLOSE
+ ///
+ Close = 0x3C2,
+ ///
+ /// MIM_DATA
+ ///
+ Data = 0x3C3,
+ ///
+ /// MIM_LONGDATA
+ ///
+ LongData = 0x3C4,
+ ///
+ /// MIM_ERROR
+ ///
+ Error = 0x3C5,
+ ///
+ /// MIM_LONGERROR
+ ///
+ LongError = 0x3C6,
+ ///
+ /// MIM_MOREDATA
+ ///
+ MoreData = 0x3CC,
+ }
+
+
+
+ public enum MidiOutMessage
+ {
+ ///
+ /// MOM_OPEN
+ ///
+ Open = 0x3C7,
+ ///
+ /// MOM_CLOSE
+ ///
+ Close = 0x3C8,
+ ///
+ /// MOM_DONE
+ ///
+ Done = 0x3C9
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd798460%28VS.85%29.aspx
+ public delegate void MidiInCallback(IntPtr midiInHandle, MidiInMessage message, IntPtr userData, IntPtr messageParameter1, IntPtr messageParameter2);
+
+ // http://msdn.microsoft.com/en-us/library/dd798478%28VS.85%29.aspx
+ public delegate void MidiOutCallback(IntPtr midiInHandle, MidiOutMessage message, IntPtr userData, IntPtr messageParameter1, IntPtr messageParameter2);
+
+ // http://msdn.microsoft.com/en-us/library/dd798446%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiConnect(IntPtr hMidiIn, IntPtr hMidiOut, IntPtr pReserved);
+
+ // http://msdn.microsoft.com/en-us/library/dd798447%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiDisconnect(IntPtr hMidiIn, IntPtr hMidiOut, IntPtr pReserved);
+
+ // http://msdn.microsoft.com/en-us/library/dd798450%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInAddBuffer(IntPtr hMidiIn, [MarshalAs(UnmanagedType.Struct)] ref MIDIHDR lpMidiInHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798452%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInClose(IntPtr hMidiIn);
+
+ // http://msdn.microsoft.com/en-us/library/dd798453%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Auto)]
+ public static extern MmResult midiInGetDevCaps(IntPtr deviceId, out MidiInCapabilities capabilities, int size);
+
+ // http://msdn.microsoft.com/en-us/library/dd798454%28VS.85%29.aspx
+ // TODO: review this, probably doesn't work
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInGetErrorText(int err, string lpText, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798455%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInGetID(IntPtr hMidiIn, out int lpuDeviceId);
+
+ // http://msdn.microsoft.com/en-us/library/dd798456%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern int midiInGetNumDevs();
+
+ // http://msdn.microsoft.com/en-us/library/dd798457%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInMessage(IntPtr hMidiIn, int msg, IntPtr dw1, IntPtr dw2);
+
+ // http://msdn.microsoft.com/en-us/library/dd798458%28VS.85%29.aspx
+ [DllImport("winmm.dll", EntryPoint = "midiInOpen")]
+ public static extern MmResult midiInOpen(out IntPtr hMidiIn, IntPtr uDeviceID, MidiInCallback callback, IntPtr dwInstance, int dwFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd798458%28VS.85%29.aspx
+ [DllImport("winmm.dll", EntryPoint = "midiInOpen")]
+ public static extern MmResult midiInOpenWindow(out IntPtr hMidiIn, IntPtr uDeviceID, IntPtr callbackWindowHandle, IntPtr dwInstance, int dwFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd798459%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInPrepareHeader(IntPtr hMidiIn, [MarshalAs(UnmanagedType.Struct)] ref MIDIHDR lpMidiInHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798461%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInReset(IntPtr hMidiIn);
+
+ // http://msdn.microsoft.com/en-us/library/dd798462%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInStart(IntPtr hMidiIn);
+
+ // http://msdn.microsoft.com/en-us/library/dd798463%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInStop(IntPtr hMidiIn);
+
+ // http://msdn.microsoft.com/en-us/library/dd798464%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiInUnprepareHeader(IntPtr hMidiIn, [MarshalAs(UnmanagedType.Struct)] ref MIDIHDR lpMidiInHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798465%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutCacheDrumPatches(IntPtr hMidiOut, int uPatch, IntPtr lpKeyArray, int uFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd798466%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutCachePatches(IntPtr hMidiOut, int uBank, IntPtr lpPatchArray, int uFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd798468%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutClose(IntPtr hMidiOut);
+
+ // http://msdn.microsoft.com/en-us/library/dd798469%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Auto)]
+ public static extern MmResult midiOutGetDevCaps(IntPtr deviceNumber, out MidiOutCapabilities caps, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798470%28VS.85%29.aspx
+ // TODO: review, probably doesn't work
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutGetErrorText(IntPtr err, string lpText, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798471%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutGetID(IntPtr hMidiOut, out int lpuDeviceID);
+
+ // http://msdn.microsoft.com/en-us/library/dd798472%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern int midiOutGetNumDevs();
+
+ // http://msdn.microsoft.com/en-us/library/dd798473%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutGetVolume(IntPtr uDeviceID, ref int lpdwVolume);
+
+ // http://msdn.microsoft.com/en-us/library/dd798474%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutLongMsg(IntPtr hMidiOut, [MarshalAs(UnmanagedType.Struct)] ref MIDIHDR lpMidiOutHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798475%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutMessage(IntPtr hMidiOut, int msg, IntPtr dw1, IntPtr dw2);
+
+ // http://msdn.microsoft.com/en-us/library/dd798476%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutOpen(out IntPtr lphMidiOut, IntPtr uDeviceID, MidiOutCallback dwCallback, IntPtr dwInstance, int dwFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd798477%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutPrepareHeader(IntPtr hMidiOut, [MarshalAs(UnmanagedType.Struct)] ref MIDIHDR lpMidiOutHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798479%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutReset(IntPtr hMidiOut);
+
+ // http://msdn.microsoft.com/en-us/library/dd798480%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutSetVolume(IntPtr hMidiOut, int dwVolume);
+
+ // http://msdn.microsoft.com/en-us/library/dd798481%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutShortMsg(IntPtr hMidiOut, int dwMsg);
+
+ // http://msdn.microsoft.com/en-us/library/dd798482%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiOutUnprepareHeader(IntPtr hMidiOut, [MarshalAs(UnmanagedType.Struct)] ref MIDIHDR lpMidiOutHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd798485%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamClose(IntPtr hMidiStream);
+
+ // http://msdn.microsoft.com/en-us/library/dd798486%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamOpen(out IntPtr hMidiStream, IntPtr puDeviceID, int cMidi, IntPtr dwCallback, IntPtr dwInstance, int fdwOpen);
+
+ // http://msdn.microsoft.com/en-us/library/dd798487%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamOut(IntPtr hMidiStream, [MarshalAs(UnmanagedType.Struct)] ref MIDIHDR pmh, int cbmh);
+
+ // http://msdn.microsoft.com/en-us/library/dd798488%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamPause(IntPtr hMidiStream);
+
+ // http://msdn.microsoft.com/en-us/library/dd798489%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamPosition(IntPtr hMidiStream, [MarshalAs(UnmanagedType.Struct)] ref MMTIME lpmmt, int cbmmt);
+
+ // http://msdn.microsoft.com/en-us/library/dd798490%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamProperty(IntPtr hMidiStream, IntPtr lppropdata, int dwProperty);
+
+ // http://msdn.microsoft.com/en-us/library/dd798491%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamRestart(IntPtr hMidiStream);
+
+ // http://msdn.microsoft.com/en-us/library/dd798492%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult midiStreamStop(IntPtr hMidiStream);
+
+ // TODO: this is general MM interop
+ public const int CALLBACK_FUNCTION = 0x30000;
+ public const int CALLBACK_NULL = 0;
+
+ // http://msdn.microsoft.com/en-us/library/dd757347%28VS.85%29.aspx
+ // TODO: not sure this is right
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MMTIME
+ {
+ public int wType;
+ public int u;
+ }
+
+ // TODO: check for ANSI strings in these structs
+ // TODO: check for WORD params
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MIDIEVENT
+ {
+ public int dwDeltaTime;
+ public int dwStreamID;
+ public int dwEvent;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
+ public int dwParms;
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd798449%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MIDIHDR
+ {
+ public IntPtr lpData; // LPSTR
+ public int dwBufferLength; // DWORD
+ public int dwBytesRecorded; // DWORD
+ public IntPtr dwUser; // DWORD_PTR
+ public int dwFlags; // DWORD
+ public IntPtr lpNext; // struct mididhdr_tag *
+ public IntPtr reserved; // DWORD_PTR
+ public int dwOffset; // DWORD
+ // n.b. MSDN documentation incorrect, see mmsystem.h
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
+ public IntPtr[] dwReserved; // DWORD_PTR dwReserved[8]
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MIDIPROPTEMPO
+ {
+ public int cbStruct;
+ public int dwTempo;
+ }
+ }
+}
diff --git a/NAudio/Midi/MidiMessage.cs b/NAudio/Midi/MidiMessage.cs
new file mode 100644
index 00000000..0db7d3e6
--- /dev/null
+++ b/NAudio/Midi/MidiMessage.cs
@@ -0,0 +1,90 @@
+using System;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI message
+ ///
+ public class MidiMessage
+ {
+ private int rawData;
+
+ ///
+ /// Creates a new MIDI message
+ ///
+ /// Status
+ /// Data parameter 1
+ /// Data parameter 2
+ public MidiMessage(int status, int data1, int data2)
+ {
+ rawData = status + (data1 << 8) + (data2 << 16);
+ }
+
+ ///
+ /// Creates a new MIDI message from a raw message
+ ///
+ /// A packed MIDI message from an MMIO function
+ public MidiMessage(int rawData)
+ {
+ this.rawData = rawData;
+ }
+
+ ///
+ /// Creates a Note On message
+ ///
+ /// Note number
+ /// Volume
+ /// MIDI channel
+ /// A new MidiMessage object
+ public static MidiMessage StartNote(int note, int volume, int channel)
+ {
+ return new MidiMessage((int)MidiCommandCode.NoteOn + channel - 1, note, volume);
+ }
+
+ ///
+ /// Creates a Note Off message
+ ///
+ /// Note number
+ /// Volume
+ /// MIDI channel (1-16)
+ /// A new MidiMessage object
+ public static MidiMessage StopNote(int note, int volume, int channel)
+ {
+ return new MidiMessage((int)MidiCommandCode.NoteOff + channel - 1, note, volume);
+ }
+
+ ///
+ /// Creates a patch change message
+ ///
+ /// The patch number
+ /// The MIDI channel number (1-16)
+ /// A new MidiMessageObject
+ public static MidiMessage ChangePatch(int patch, int channel)
+ {
+ return new MidiMessage((int)MidiCommandCode.PatchChange + channel - 1, patch, 0);
+ }
+
+ ///
+ /// Creates a Control Change message
+ ///
+ /// The controller number to change
+ /// The value to set the controller to
+ /// The MIDI channel number (1-16)
+ /// A new MidiMessageObject
+ public static MidiMessage ChangeControl(int controller, int value, int channel)
+ {
+ return new MidiMessage((int)MidiCommandCode.ControlChange + channel - 1, controller, value);
+ }
+
+ ///
+ /// Returns the raw MIDI message data
+ ///
+ public int RawData
+ {
+ get
+ {
+ return rawData;
+ }
+ }
+ }
+}
diff --git a/NAudio/Midi/MidiOut.cs b/NAudio/Midi/MidiOut.cs
new file mode 100644
index 00000000..eb943963
--- /dev/null
+++ b/NAudio/Midi/MidiOut.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI out device
+ ///
+ public class MidiOut : IDisposable
+ {
+ private IntPtr hMidiOut = IntPtr.Zero;
+ private bool disposed = false;
+ MidiInterop.MidiOutCallback callback;
+
+ ///
+ /// Gets the number of MIDI devices available in the system
+ ///
+ public static int NumberOfDevices
+ {
+ get
+ {
+ return MidiInterop.midiOutGetNumDevs();
+ }
+ }
+
+ ///
+ /// Gets the MIDI Out device info
+ ///
+ public static MidiOutCapabilities DeviceInfo(int midiOutDeviceNumber)
+ {
+ MidiOutCapabilities caps = new MidiOutCapabilities();
+ int structSize = Marshal.SizeOf(caps);
+ MmException.Try(MidiInterop.midiOutGetDevCaps((IntPtr)midiOutDeviceNumber, out caps, structSize), "midiOutGetDevCaps");
+ return caps;
+ }
+
+
+ ///
+ /// Opens a specified MIDI out device
+ ///
+ /// The device number
+ public MidiOut(int deviceNo)
+ {
+ this.callback = new MidiInterop.MidiOutCallback(Callback);
+ MmException.Try(MidiInterop.midiOutOpen(out hMidiOut, (IntPtr)deviceNo, callback, IntPtr.Zero, MidiInterop.CALLBACK_FUNCTION), "midiOutOpen");
+ }
+
+ ///
+ /// Closes this MIDI out device
+ ///
+ public void Close()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Closes this MIDI out device
+ ///
+ public void Dispose()
+ {
+ GC.KeepAlive(callback);
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Gets or sets the volume for this MIDI out device
+ ///
+ public int Volume
+ {
+ // TODO: Volume can be accessed by device ID
+ get
+ {
+ int volume = 0;
+ MmException.Try(MidiInterop.midiOutGetVolume(hMidiOut,ref volume),"midiOutGetVolume");
+ return volume;
+ }
+ set
+ {
+ MmException.Try(MidiInterop.midiOutSetVolume(hMidiOut,value),"midiOutSetVolume");
+ }
+ }
+
+ ///
+ /// Resets the MIDI out device
+ ///
+ public void Reset()
+ {
+ MmException.Try(MidiInterop.midiOutReset(hMidiOut),"midiOutReset");
+ }
+
+ ///
+ /// Sends a MIDI out message
+ ///
+ /// Message
+ /// Parameter 1
+ /// Parameter 2
+ public void SendDriverMessage(int message, int param1, int param2)
+ {
+ MmException.Try(MidiInterop.midiOutMessage(hMidiOut,message,(IntPtr)param1,(IntPtr)param2),"midiOutMessage");
+ }
+
+ ///
+ /// Sends a MIDI message to the MIDI out device
+ ///
+ /// The message to send
+ public void Send(int message)
+ {
+ MmException.Try(MidiInterop.midiOutShortMsg(hMidiOut,message),"midiOutShortMsg");
+ }
+
+ ///
+ /// Closes the MIDI out device
+ ///
+ /// True if called from Dispose
+ protected virtual void Dispose(bool disposing)
+ {
+ if(!this.disposed)
+ {
+ //if(disposing) Components.Dispose();
+ MidiInterop.midiOutClose(hMidiOut);
+ }
+ disposed = true;
+ }
+
+ private void Callback(IntPtr midiInHandle, MidiInterop.MidiOutMessage message, IntPtr userData, IntPtr messageParameter1, IntPtr messageParameter2)
+ {
+ }
+
+ ///
+ /// Send a long message, for example sysex.
+ ///
+ /// The bytes to send.
+ public void SendBuffer(byte[] byteBuffer)
+ {
+ var header = new MidiInterop.MIDIHDR();
+ header.lpData = Marshal.AllocHGlobal(byteBuffer.Length);
+ Marshal.Copy(byteBuffer, 0, header.lpData, byteBuffer.Length);
+
+ header.dwBufferLength = byteBuffer.Length;
+ header.dwBytesRecorded = byteBuffer.Length;
+ int size = Marshal.SizeOf(header);
+ MidiInterop.midiOutPrepareHeader(this.hMidiOut, ref header, size);
+ var errcode = MidiInterop.midiOutLongMsg(this.hMidiOut, ref header, size);
+ if (errcode != MmResult.NoError)
+ {
+ MidiInterop.midiOutUnprepareHeader(this.hMidiOut, ref header, size);
+ }
+ Marshal.FreeHGlobal(header.lpData);
+ }
+
+ ///
+ /// Cleanup
+ ///
+ ~MidiOut()
+ {
+ System.Diagnostics.Debug.Assert(false);
+ Dispose(false);
+ }
+ }
+}
diff --git a/NAudio/Midi/MidiOutCapabilities.cs b/NAudio/Midi/MidiOutCapabilities.cs
new file mode 100644
index 00000000..dda32cb4
--- /dev/null
+++ b/NAudio/Midi/MidiOutCapabilities.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Midi
+{
+ ///
+ /// class representing the capabilities of a MIDI out device
+ /// MIDIOUTCAPS: http://msdn.microsoft.com/en-us/library/dd798467%28VS.85%29.aspx
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public struct MidiOutCapabilities
+ {
+ Int16 manufacturerId;
+ Int16 productId;
+ int driverVersion;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxProductNameLength)]
+ string productName;
+ Int16 wTechnology;
+ Int16 wVoices;
+ Int16 wNotes;
+ UInt16 wChannelMask;
+ MidiOutCapabilityFlags dwSupport;
+
+ const int MaxProductNameLength = 32; // max product name length (including NULL)
+
+ [Flags]
+ enum MidiOutCapabilityFlags
+ {
+ ///
+ /// MIDICAPS_VOLUME
+ ///
+ Volume = 1,
+ ///
+ /// separate left-right volume control
+ /// MIDICAPS_LRVOLUME
+ ///
+ LeftRightVolume = 2,
+ ///
+ /// MIDICAPS_CACHE
+ ///
+ PatchCaching = 4,
+ ///
+ /// MIDICAPS_STREAM
+ /// driver supports midiStreamOut directly
+ ///
+ Stream = 8,
+ }
+
+ ///
+ /// Gets the manufacturer of this device
+ ///
+ public Manufacturers Manufacturer
+ {
+ get
+ {
+ return (Manufacturers)manufacturerId;
+ }
+ }
+
+ ///
+ /// Gets the product identifier (manufacturer specific)
+ ///
+ public short ProductId
+ {
+ get
+ {
+ return productId;
+ }
+ }
+
+ ///
+ /// Gets the product name
+ ///
+ public String ProductName
+ {
+ get
+ {
+ return productName;
+ }
+ }
+
+ ///
+ /// Returns the number of supported voices
+ ///
+ public int Voices
+ {
+ get
+ {
+ return wVoices;
+ }
+ }
+
+ ///
+ /// Gets the polyphony of the device
+ ///
+ public int Notes
+ {
+ get
+ {
+ return wNotes;
+ }
+ }
+
+ ///
+ /// Returns true if the device supports all channels
+ ///
+ public bool SupportsAllChannels
+ {
+ get
+ {
+ return wChannelMask == 0xFFFF;
+ }
+ }
+
+ ///
+ /// Queries whether a particular channel is supported
+ ///
+ /// Channel number to test
+ /// True if the channel is supported
+ public bool SupportsChannel(int channel)
+ {
+ return (wChannelMask & (1 << (channel - 1))) > 0;
+ }
+
+ ///
+ /// Returns true if the device supports patch caching
+ ///
+ public bool SupportsPatchCaching
+ {
+ get
+ {
+ return (dwSupport & MidiOutCapabilityFlags.PatchCaching) != 0;
+ }
+ }
+
+ ///
+ /// Returns true if the device supports separate left and right volume
+ ///
+ public bool SupportsSeparateLeftAndRightVolume
+ {
+ get
+ {
+ return (dwSupport & MidiOutCapabilityFlags.LeftRightVolume) != 0;
+ }
+ }
+
+ ///
+ /// Returns true if the device supports MIDI stream out
+ ///
+ public bool SupportsMidiStreamOut
+ {
+ get
+ {
+ return (dwSupport & MidiOutCapabilityFlags.Stream) != 0;
+ }
+ }
+
+ ///
+ /// Returns true if the device supports volume control
+ ///
+ public bool SupportsVolumeControl
+ {
+ get
+ {
+ return (dwSupport & MidiOutCapabilityFlags.Volume) != 0;
+ }
+ }
+
+ ///
+ /// Returns the type of technology used by this MIDI out device
+ ///
+ public MidiOutTechnology Technology
+ {
+ get
+ {
+ return (MidiOutTechnology)wTechnology;
+ }
+ }
+
+ }
+}
diff --git a/NAudio/Midi/MidiOutTechnology.cs b/NAudio/Midi/MidiOutTechnology.cs
new file mode 100644
index 00000000..2e3f01d6
--- /dev/null
+++ b/NAudio/Midi/MidiOutTechnology.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents the different types of technology used by a MIDI out device
+ ///
+ /// from mmsystem.h
+ public enum MidiOutTechnology
+ {
+ /// The device is a MIDI port
+ MidiPort = 1,
+ /// The device is a MIDI synth
+ Synth = 2,
+ /// The device is a square wave synth
+ SquareWaveSynth = 3,
+ /// The device is an FM synth
+ FMSynth = 4,
+ /// The device is a MIDI mapper
+ MidiMapper = 5,
+ /// The device is a WaveTable synth
+ WaveTableSynth = 6,
+ /// The device is a software synth
+ SoftwareSynth = 7
+ }
+}
diff --git a/NAudio/Midi/NoteEvent.cs b/NAudio/Midi/NoteEvent.cs
new file mode 100644
index 00000000..f9942463
--- /dev/null
+++ b/NAudio/Midi/NoteEvent.cs
@@ -0,0 +1,185 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a note MIDI event
+ ///
+ public class NoteEvent : MidiEvent
+ {
+ private int noteNumber;
+ private int velocity;
+
+ ///
+ /// Reads a NoteEvent from a stream of MIDI data
+ ///
+ /// Binary Reader for the stream
+ public NoteEvent(BinaryReader br)
+ {
+ NoteNumber = br.ReadByte();
+ velocity = br.ReadByte();
+ // it seems it is possible for cubase
+ // to output some notes with a NoteOff velocity > 127
+ if (velocity > 127)
+ {
+ velocity = 127;
+ }
+ }
+
+ ///
+ /// Creates a MIDI Note Event with specified parameters
+ ///
+ /// Absolute time of this event
+ /// MIDI channel number
+ /// MIDI command code
+ /// MIDI Note Number
+ /// MIDI Note Velocity
+ public NoteEvent(long absoluteTime, int channel, MidiCommandCode commandCode, int noteNumber, int velocity)
+ : base(absoluteTime, channel, commandCode)
+ {
+ this.NoteNumber = noteNumber;
+ this.Velocity = velocity;
+ }
+
+ private static readonly string[] NoteNames = new string[] { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
+
+ ///
+ ///
+ ///
+ public override int GetAsShortMessage()
+ {
+ return base.GetAsShortMessage() + (noteNumber << 8) + (velocity << 16);
+ }
+
+ ///
+ /// The MIDI note number
+ ///
+ public virtual int NoteNumber
+ {
+ get
+ {
+ return noteNumber;
+ }
+ set
+ {
+ if (value < 0 || value > 127)
+ {
+ throw new ArgumentOutOfRangeException("value", "Note number must be in the range 0-127");
+ }
+ noteNumber = value;
+ }
+ }
+
+ ///
+ /// The note velocity
+ ///
+ public int Velocity
+ {
+ get
+ {
+ return velocity;
+ }
+ set
+ {
+ if (value < 0 || value > 127)
+ {
+ throw new ArgumentOutOfRangeException("value", "Velocity must be in the range 0-127");
+ }
+ velocity = value;
+ }
+ }
+
+ ///
+ /// The note name
+ ///
+ public string NoteName
+ {
+ get
+ {
+ if ((Channel == 16) || (Channel == 10))
+ {
+ switch (noteNumber)
+ {
+ case 35: return "Acoustic Bass Drum";
+ case 36: return "Bass Drum 1";
+ case 37: return "Side Stick";
+ case 38: return "Acoustic Snare";
+ case 39: return "Hand Clap";
+ case 40: return "Electric Snare";
+ case 41: return "Low Floor Tom";
+ case 42: return "Closed Hi-Hat";
+ case 43: return "High Floor Tom";
+ case 44: return "Pedal Hi-Hat";
+ case 45: return "Low Tom";
+ case 46: return "Open Hi-Hat";
+ case 47: return "Low-Mid Tom";
+ case 48: return "Hi-Mid Tom";
+ case 49: return "Crash Cymbal 1";
+ case 50: return "High Tom";
+ case 51: return "Ride Cymbal 1";
+ case 52: return "Chinese Cymbal";
+ case 53: return "Ride Bell";
+ case 54: return "Tambourine";
+ case 55: return "Splash Cymbal";
+ case 56: return "Cowbell";
+ case 57: return "Crash Cymbal 2";
+ case 58: return "Vibraslap";
+ case 59: return "Ride Cymbal 2";
+ case 60: return "Hi Bongo";
+ case 61: return "Low Bongo";
+ case 62: return "Mute Hi Conga";
+ case 63: return "Open Hi Conga";
+ case 64: return "Low Conga";
+ case 65: return "High Timbale";
+ case 66: return "Low Timbale";
+ case 67: return "High Agogo";
+ case 68: return "Low Agogo";
+ case 69: return "Cabasa";
+ case 70: return "Maracas";
+ case 71: return "Short Whistle";
+ case 72: return "Long Whistle";
+ case 73: return "Short Guiro";
+ case 74: return "Long Guiro";
+ case 75: return "Claves";
+ case 76: return "Hi Wood Block";
+ case 77: return "Low Wood Block";
+ case 78: return "Mute Cuica";
+ case 79: return "Open Cuica";
+ case 80: return "Mute Triangle";
+ case 81: return "Open Triangle";
+ default: return String.Format("Drum {0}", noteNumber);
+ }
+ }
+ else
+ {
+ int octave = noteNumber / 12;
+ return String.Format("{0}{1}", NoteNames[noteNumber % 12], octave);
+ }
+ }
+ }
+
+ ///
+ /// Describes the Note Event
+ ///
+ /// Note event as a string
+ public override string ToString()
+ {
+ return String.Format("{0} {1} Vel:{2}",
+ base.ToString(),
+ this.NoteName,
+ this.Velocity);
+ }
+
+ ///
+ ///
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write((byte)noteNumber);
+ writer.Write((byte)velocity);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/NoteOnEvent.cs b/NAudio/Midi/NoteOnEvent.cs
new file mode 100644
index 00000000..852ff287
--- /dev/null
+++ b/NAudio/Midi/NoteOnEvent.cs
@@ -0,0 +1,145 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI note on event
+ ///
+ public class NoteOnEvent : NoteEvent
+ {
+ private NoteEvent offEvent;
+
+ ///
+ /// Reads a new Note On event from a stream of MIDI data
+ ///
+ /// Binary reader on the MIDI data stream
+ public NoteOnEvent(BinaryReader br)
+ : base(br)
+ {
+ }
+
+ ///
+ /// Creates a NoteOn event with specified parameters
+ ///
+ /// Absolute time of this event
+ /// MIDI channel number
+ /// MIDI note number
+ /// MIDI note velocity
+ /// MIDI note duration
+ public NoteOnEvent(long absoluteTime, int channel, int noteNumber,
+ int velocity, int duration)
+ : base(absoluteTime, channel, MidiCommandCode.NoteOn, noteNumber, velocity)
+ {
+ this.OffEvent = new NoteEvent(absoluteTime, channel, MidiCommandCode.NoteOff,
+ noteNumber, 0);
+ NoteLength = duration;
+ }
+
+ ///
+ /// The associated Note off event
+ ///
+ public NoteEvent OffEvent
+ {
+ get
+ {
+ return offEvent;
+ }
+ set
+ {
+ if (!MidiEvent.IsNoteOff(value))
+ {
+ throw new ArgumentException("OffEvent must be a valid MIDI note off event");
+ }
+ if (value.NoteNumber != this.NoteNumber)
+ {
+ throw new ArgumentException("Note Off Event must be for the same note number");
+ }
+ if (value.Channel != this.Channel)
+ {
+ throw new ArgumentException("Note Off Event must be for the same channel");
+ }
+ offEvent = value;
+
+ }
+ }
+
+ ///
+ /// Get or set the Note Number, updating the off event at the same time
+ ///
+ public override int NoteNumber
+ {
+ get
+ {
+ return base.NoteNumber;
+ }
+ set
+ {
+ base.NoteNumber = value;
+ if (OffEvent != null)
+ {
+ OffEvent.NoteNumber = NoteNumber;
+ }
+ }
+ }
+
+ ///
+ /// Get or set the channel, updating the off event at the same time
+ ///
+ public override int Channel
+ {
+ get
+ {
+ return base.Channel;
+ }
+ set
+ {
+ base.Channel = value;
+ if (OffEvent != null)
+ {
+ OffEvent.Channel = Channel;
+ }
+ }
+ }
+
+ ///
+ /// The duration of this note
+ ///
+ ///
+ /// There must be a note off event
+ ///
+ public int NoteLength
+ {
+ get
+ {
+ return (int)(offEvent.AbsoluteTime - this.AbsoluteTime);
+ }
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentException("NoteLength must be 0 or greater");
+ }
+ offEvent.AbsoluteTime = this.AbsoluteTime + value;
+ }
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override string ToString()
+ {
+ if ((this.Velocity == 0) && (OffEvent == null))
+ {
+ return String.Format("{0} (Note Off)",
+ base.ToString());
+ }
+ return String.Format("{0} Len: {1}",
+ base.ToString(),
+ (this.OffEvent == null) ? "?" : this.NoteLength.ToString());
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/PatchChangeEvent.cs b/NAudio/Midi/PatchChangeEvent.cs
new file mode 100644
index 00000000..2f09f612
--- /dev/null
+++ b/NAudio/Midi/PatchChangeEvent.cs
@@ -0,0 +1,119 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI patch change event
+ ///
+ public class PatchChangeEvent : MidiEvent
+ {
+ private byte patch;
+
+ ///
+ /// Gets the default MIDI instrument names
+ ///
+ public static string GetPatchName(int patchNumber)
+ {
+ return patchNames[patchNumber];
+ }
+
+ // TODO: localize
+ private static readonly string[] patchNames = new string[]
+ {
+ "Acoustic Grand","Bright Acoustic","Electric Grand","Honky-Tonk","Electric Piano 1","Electric Piano 2","Harpsichord","Clav",
+ "Celesta","Glockenspiel","Music Box","Vibraphone","Marimba","Xylophone","Tubular Bells","Dulcimer",
+ "Drawbar Organ","Percussive Organ","Rock Organ","Church Organ","Reed Organ","Accoridan","Harmonica","Tango Accordian",
+ "Acoustic Guitar(nylon)","Acoustic Guitar(steel)","Electric Guitar(jazz)","Electric Guitar(clean)","Electric Guitar(muted)","Overdriven Guitar","Distortion Guitar","Guitar Harmonics",
+ "Acoustic Bass","Electric Bass(finger)","Electric Bass(pick)","Fretless Bass","Slap Bass 1","Slap Bass 2","Synth Bass 1","Synth Bass 2",
+ "Violin","Viola","Cello","Contrabass","Tremolo Strings","Pizzicato Strings","Orchestral Strings","Timpani",
+ "String Ensemble 1","String Ensemble 2","SynthStrings 1","SynthStrings 2","Choir Aahs","Voice Oohs","Synth Voice","Orchestra Hit",
+ "Trumpet","Trombone","Tuba","Muted Trumpet","French Horn","Brass Section","SynthBrass 1","SynthBrass 2",
+ "Soprano Sax","Alto Sax","Tenor Sax","Baritone Sax","Oboe","English Horn","Bassoon","Clarinet",
+ "Piccolo","Flute","Recorder","Pan Flute","Blown Bottle","Skakuhachi","Whistle","Ocarina",
+ "Lead 1 (square)","Lead 2 (sawtooth)","Lead 3 (calliope)","Lead 4 (chiff)","Lead 5 (charang)","Lead 6 (voice)","Lead 7 (fifths)","Lead 8 (bass+lead)",
+ "Pad 1 (new age)","Pad 2 (warm)","Pad 3 (polysynth)","Pad 4 (choir)","Pad 5 (bowed)","Pad 6 (metallic)","Pad 7 (halo)","Pad 8 (sweep)",
+ "FX 1 (rain)","FX 2 (soundtrack)","FX 3 (crystal)","FX 4 (atmosphere)","FX 5 (brightness)","FX 6 (goblins)","FX 7 (echoes)","FX 8 (sci-fi)",
+ "Sitar","Banjo","Shamisen","Koto","Kalimba","Bagpipe","Fiddle","Shanai",
+ "Tinkle Bell","Agogo","Steel Drums","Woodblock","Taiko Drum","Melodic Tom","Synth Drum","Reverse Cymbal",
+ "Guitar Fret Noise","Breath Noise","Seashore","Bird Tweet","Telephone Ring","Helicopter","Applause","Gunshot"
+ };
+
+ ///
+ /// Reads a new patch change event from a MIDI stream
+ ///
+ /// Binary reader for the MIDI stream
+ public PatchChangeEvent(BinaryReader br)
+ {
+ patch = br.ReadByte();
+ if ((patch & 0x80) != 0)
+ {
+ // TODO: might be a follow-on
+ throw new FormatException("Invalid patch");
+ }
+ }
+
+ ///
+ /// Creates a new patch change event
+ ///
+ /// Time of the event
+ /// Channel number
+ /// Patch number
+ public PatchChangeEvent(long absoluteTime, int channel, int patchNumber)
+ : base(absoluteTime, channel, MidiCommandCode.PatchChange)
+ {
+ this.Patch = patchNumber;
+ }
+
+ ///
+ /// The Patch Number
+ ///
+ public int Patch
+ {
+ get
+ {
+ return patch;
+ }
+ set
+ {
+ if (value < 0 || value > 127)
+ {
+ throw new ArgumentOutOfRangeException("value", "Patch number must be in the range 0-127");
+ }
+ patch = (byte)value;
+ }
+ }
+
+ ///
+ /// Describes this patch change event
+ ///
+ /// String describing the patch change event
+ public override string ToString()
+ {
+ return String.Format("{0} {1}",
+ base.ToString(),
+ GetPatchName(this.patch));
+ }
+
+ ///
+ /// Gets as a short message for sending with the midiOutShortMsg API
+ ///
+ /// short message
+ public override int GetAsShortMessage()
+ {
+ return base.GetAsShortMessage() + (this.patch << 8);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write(patch);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/PitchWheelChangeEvent.cs b/NAudio/Midi/PitchWheelChangeEvent.cs
new file mode 100644
index 00000000..4e4dfb14
--- /dev/null
+++ b/NAudio/Midi/PitchWheelChangeEvent.cs
@@ -0,0 +1,99 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI pitch wheel change event
+ ///
+ public class PitchWheelChangeEvent : MidiEvent
+ {
+ private int pitch;
+
+ ///
+ /// Reads a pitch wheel change event from a MIDI stream
+ ///
+ /// The MIDI stream to read from
+ public PitchWheelChangeEvent(BinaryReader br)
+ {
+ byte b1 = br.ReadByte();
+ byte b2 = br.ReadByte();
+ if((b1 & 0x80) != 0)
+ {
+ // TODO: might be a follow-on
+ throw new FormatException("Invalid pitchwheelchange byte 1");
+ }
+ if((b2 & 0x80) != 0)
+ {
+ throw new FormatException("Invalid pitchwheelchange byte 2");
+ }
+
+ pitch = b1 + (b2 << 7); // 0x2000 is normal
+ }
+
+ ///
+ /// Creates a new pitch wheel change event
+ ///
+ /// Absolute event time
+ /// Channel
+ /// Pitch wheel value
+ public PitchWheelChangeEvent(long absoluteTime, int channel, int pitchWheel)
+ : base(absoluteTime, channel, MidiCommandCode.PitchWheelChange)
+ {
+ Pitch = pitchWheel;
+ }
+
+ ///
+ /// Describes this pitch wheel change event
+ ///
+ /// String describing this pitch wheel change event
+ public override string ToString()
+ {
+ return String.Format("{0} Pitch {1} ({2})",
+ base.ToString(),
+ this.pitch,
+ this.pitch - 0x2000);
+ }
+
+ ///
+ /// Pitch Wheel Value 0 is minimum, 0x2000 (8192) is default, 0x4000 (16384) is maximum
+ ///
+ public int Pitch
+ {
+ get
+ {
+ return pitch;
+ }
+ set
+ {
+ if (value < 0 || value > 0x4000)
+ {
+ throw new ArgumentOutOfRangeException("value", "Pitch value must be in the range 0 - 0x4000");
+ }
+ pitch = value;
+ }
+ }
+
+ ///
+ /// Gets a short message
+ ///
+ /// Integer to sent as short message
+ public override int GetAsShortMessage()
+ {
+ return base.GetAsShortMessage() + ((pitch & 0x7f) << 8) + (((pitch >> 7) & 0x7f) << 16);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write((byte)(pitch & 0x7f));
+ writer.Write((byte)((pitch >> 7) & 0x7f));
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/SequencerSpecificEvent.cs b/NAudio/Midi/SequencerSpecificEvent.cs
new file mode 100644
index 00000000..b91516b0
--- /dev/null
+++ b/NAudio/Midi/SequencerSpecificEvent.cs
@@ -0,0 +1,79 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a Sequencer Specific event
+ ///
+ public class SequencerSpecificEvent : MetaEvent
+ {
+ private byte[] data;
+
+ ///
+ /// Reads a new sequencer specific event from a MIDI stream
+ ///
+ /// The MIDI stream
+ /// The data length
+ public SequencerSpecificEvent(BinaryReader br, int length)
+ {
+ this.data = br.ReadBytes(length);
+ }
+
+ ///
+ /// Creates a new Sequencer Specific event
+ ///
+ /// The sequencer specific data
+ /// Absolute time of this event
+ public SequencerSpecificEvent(byte[] data, long absoluteTime)
+ : base(MetaEventType.SequencerSpecific, data.Length, absoluteTime)
+ {
+ this.data = data;
+ }
+
+ ///
+ /// The contents of this sequencer specific
+ ///
+ public byte[] Data
+ {
+ get
+ {
+ return this.data;
+ }
+ set
+ {
+ this.data = value;
+ this.metaDataLength = this.data.Length;
+ }
+ }
+
+ ///
+ /// Describes this MIDI text event
+ ///
+ /// A string describing this event
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append(base.ToString());
+ sb.Append(" ");
+ foreach (var b in data)
+ {
+ sb.AppendFormat("{0:X2} ", b);
+ }
+ sb.Length--;
+ return sb.ToString();
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write(data);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/SmpteOffsetEvent.cs b/NAudio/Midi/SmpteOffsetEvent.cs
new file mode 100644
index 00000000..e5e23493
--- /dev/null
+++ b/NAudio/Midi/SmpteOffsetEvent.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.Midi
+{
+ class SmpteOffsetEvent : MetaEvent
+ {
+ private byte hours;
+ private byte minutes;
+ private byte seconds;
+ private byte frames;
+ private byte subFrames; // 100ths of a frame
+
+ ///
+ /// Reads a new time signature event from a MIDI stream
+ ///
+ /// The MIDI stream
+ /// The data length
+ public SmpteOffsetEvent(BinaryReader br,int length)
+ {
+ if(length != 5)
+ {
+ throw new FormatException(String.Format("Invalid SMPTE Offset length: Got {0}, expected 5",length));
+ }
+ hours = br.ReadByte();
+ minutes = br.ReadByte();
+ seconds = br.ReadByte();
+ frames = br.ReadByte();
+ subFrames = br.ReadByte();
+ }
+
+ ///
+ /// Hours
+ ///
+ public int Hours
+ {
+ get { return hours; }
+ }
+
+ ///
+ /// Minutes
+ ///
+ public int Minutes
+ {
+ get { return minutes; }
+ }
+
+ ///
+ /// Seconds
+ ///
+ public int Seconds
+ {
+ get { return seconds; }
+ }
+
+ ///
+ /// Frames
+ ///
+ public int Frames
+ {
+ get { return frames; }
+ }
+
+ ///
+ /// SubFrames
+ ///
+ public int SubFrames
+ {
+ get { return subFrames; }
+ }
+
+
+ ///
+ /// Describes this time signature event
+ ///
+ /// A string describing this event
+ public override string ToString()
+ {
+ return String.Format("{0} {1}:{2}:{3}:{4}:{5}",
+ base.ToString(),hours,minutes,seconds,frames,subFrames);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write(hours);
+ writer.Write(minutes);
+ writer.Write(seconds);
+ writer.Write(frames);
+ writer.Write(subFrames);
+ }
+ }
+}
+
diff --git a/NAudio/Midi/SysexEvent.cs b/NAudio/Midi/SysexEvent.cs
new file mode 100644
index 00000000..ac0a5af2
--- /dev/null
+++ b/NAudio/Midi/SysexEvent.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI sysex message
+ ///
+ public class SysexEvent : MidiEvent
+ {
+ private byte[] data;
+ //private int length;
+
+ ///
+ /// Reads a sysex message from a MIDI stream
+ ///
+ /// Stream of MIDI data
+ /// a new sysex message
+ public static SysexEvent ReadSysexEvent(BinaryReader br)
+ {
+ SysexEvent se = new SysexEvent();
+ //se.length = ReadVarInt(br);
+ //se.data = br.ReadBytes(se.length);
+
+ List sysexData = new List();
+ bool loop = true;
+ while(loop)
+ {
+ byte b = br.ReadByte();
+ if(b == 0xF7)
+ {
+ loop = false;
+ }
+ else
+ {
+ sysexData.Add(b);
+ }
+ }
+
+ se.data = sysexData.ToArray();
+
+ return se;
+ }
+
+ ///
+ /// Describes this sysex message
+ ///
+ /// A string describing the sysex message
+ public override string ToString()
+ {
+ StringBuilder sb = new StringBuilder();
+ foreach (byte b in data)
+ {
+ sb.AppendFormat("{0:X2} ", b);
+ }
+ return String.Format("{0} Sysex: {1} bytes\r\n{2}",this.AbsoluteTime,data.Length,sb.ToString());
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ //WriteVarInt(writer,length);
+ //writer.Write(data, 0, data.Length);
+ writer.Write(data, 0, data.Length);
+ writer.Write((byte)0xF7);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/TempoEvent.cs b/NAudio/Midi/TempoEvent.cs
new file mode 100644
index 00000000..6a65808f
--- /dev/null
+++ b/NAudio/Midi/TempoEvent.cs
@@ -0,0 +1,82 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI tempo event
+ ///
+ public class TempoEvent : MetaEvent
+ {
+ private int microsecondsPerQuarterNote;
+
+ ///
+ /// Reads a new tempo event from a MIDI stream
+ ///
+ /// The MIDI stream
+ /// the data length
+ public TempoEvent(BinaryReader br,int length)
+ {
+ if(length != 3)
+ {
+ throw new FormatException("Invalid tempo length");
+ }
+ microsecondsPerQuarterNote = (br.ReadByte() << 16) + (br.ReadByte() << 8) + br.ReadByte();
+ }
+
+ ///
+ /// Creates a new tempo event with specified settings
+ ///
+ /// Microseconds per quarter note
+ /// Absolute time
+ public TempoEvent(int microsecondsPerQuarterNote, long absoluteTime)
+ : base(MetaEventType.SetTempo,3,absoluteTime)
+ {
+ this.microsecondsPerQuarterNote = microsecondsPerQuarterNote;
+ }
+
+ ///
+ /// Describes this tempo event
+ ///
+ /// String describing the tempo event
+ public override string ToString()
+ {
+ return String.Format("{0} {2}bpm ({1})",
+ base.ToString(),
+ microsecondsPerQuarterNote,
+ (60000000 / microsecondsPerQuarterNote));
+ }
+
+ ///
+ /// Microseconds per quarter note
+ ///
+ public int MicrosecondsPerQuarterNote
+ {
+ get { return microsecondsPerQuarterNote; }
+ set { microsecondsPerQuarterNote = value; }
+ }
+
+ ///
+ /// Tempo
+ ///
+ public double Tempo
+ {
+ get { return (60000000.0/microsecondsPerQuarterNote); }
+ set { microsecondsPerQuarterNote = (int) (60000000.0/value); }
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write((byte) ((microsecondsPerQuarterNote >> 16) & 0xFF));
+ writer.Write((byte) ((microsecondsPerQuarterNote >> 8) & 0xFF));
+ writer.Write((byte) (microsecondsPerQuarterNote & 0xFF));
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/TextEvent.cs b/NAudio/Midi/TextEvent.cs
new file mode 100644
index 00000000..61713287
--- /dev/null
+++ b/NAudio/Midi/TextEvent.cs
@@ -0,0 +1,76 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI text event
+ ///
+ public class TextEvent : MetaEvent
+ {
+ private string text;
+
+ ///
+ /// Reads a new text event from a MIDI stream
+ ///
+ /// The MIDI stream
+ /// The data length
+ public TextEvent(BinaryReader br,int length)
+ {
+ Encoding byteEncoding = NAudio.Utils.ByteEncoding.Instance;
+ text = byteEncoding.GetString(br.ReadBytes(length));
+ }
+
+ ///
+ /// Creates a new TextEvent
+ ///
+ /// The text in this type
+ /// MetaEvent type (must be one that is
+ /// associated with text data)
+ /// Absolute time of this event
+ public TextEvent(string text, MetaEventType metaEventType, long absoluteTime)
+ : base(metaEventType, text.Length, absoluteTime)
+ {
+ this.text = text;
+ }
+
+ ///
+ /// The contents of this text event
+ ///
+ public string Text
+ {
+ get
+ {
+ return text;
+ }
+ set
+ {
+ text = value;
+ metaDataLength = text.Length;
+ }
+ }
+
+ ///
+ /// Describes this MIDI text event
+ ///
+ /// A string describing this event
+ public override string ToString()
+ {
+ return String.Format("{0} {1}",base.ToString(),text);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ Encoding byteEncoding = NAudio.Utils.ByteEncoding.Instance;
+ byte[] encoded = byteEncoding.GetBytes(text);
+ writer.Write(encoded);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/TimeSignatureEvent.cs b/NAudio/Midi/TimeSignatureEvent.cs
new file mode 100644
index 00000000..c3f78d81
--- /dev/null
+++ b/NAudio/Midi/TimeSignatureEvent.cs
@@ -0,0 +1,152 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI time signature event
+ ///
+ public class TimeSignatureEvent : MetaEvent
+ {
+ private byte numerator;
+ private byte denominator;
+ private byte ticksInMetronomeClick;
+ private byte no32ndNotesInQuarterNote;
+
+ ///
+ /// Reads a new time signature event from a MIDI stream
+ ///
+ /// The MIDI stream
+ /// The data length
+ public TimeSignatureEvent(BinaryReader br,int length)
+ {
+ if(length != 4)
+ {
+ throw new FormatException(String.Format("Invalid time signature length: Got {0}, expected 4", length));
+ }
+ numerator = br.ReadByte();
+ denominator = br.ReadByte(); //2=quarter, 3=eigth etc
+ ticksInMetronomeClick = br.ReadByte();
+ no32ndNotesInQuarterNote = br.ReadByte();
+ }
+
+ ///
+ /// Creates a new TimeSignatureEvent
+ ///
+ /// Time at which to create this event
+ /// Numerator
+ /// Denominator
+ /// Ticks in Metronome Click
+ /// No of 32nd Notes in Quarter Click
+ public TimeSignatureEvent(long absoluteTime, int numerator, int denominator, int ticksInMetronomeClick, int no32ndNotesInQuarterNote)
+ :
+ base(MetaEventType.TimeSignature, 4, absoluteTime)
+ {
+ this.numerator = (byte)numerator;
+ this.denominator = (byte)denominator;
+ this.ticksInMetronomeClick = (byte)ticksInMetronomeClick;
+ this.no32ndNotesInQuarterNote = (byte)no32ndNotesInQuarterNote;
+ }
+
+ ///
+ /// Creates a new time signature event with the specified parameters
+ ///
+ [Obsolete("Use the constructor that has absolute time first")]
+ public TimeSignatureEvent(int numerator, int denominator, int ticksInMetronomeClick, int no32ndNotesInQuarterNote, long absoluteTime)
+ : base(MetaEventType.TimeSignature, 4, absoluteTime)
+ {
+ this.numerator = (byte) numerator;
+ this.denominator = (byte) denominator;
+ this.ticksInMetronomeClick = (byte) ticksInMetronomeClick;
+ this.no32ndNotesInQuarterNote = (byte) no32ndNotesInQuarterNote;
+ }
+
+ ///
+ /// Numerator (number of beats in a bar)
+ ///
+ public int Numerator
+ {
+ get { return numerator; }
+ }
+
+ ///
+ /// Denominator (Beat unit),
+ /// 1 means 2, 2 means 4 (crochet), 3 means 8 (quaver), 4 means 16 and 5 means 32
+ ///
+ public int Denominator
+ {
+ get { return denominator; }
+ }
+
+ ///
+ /// Ticks in a metronome click
+ ///
+ public int TicksInMetronomeClick
+ {
+ get { return ticksInMetronomeClick; }
+ }
+
+ ///
+ /// Number of 32nd notes in a quarter note
+ ///
+ public int No32ndNotesInQuarterNote
+ {
+ get { return no32ndNotesInQuarterNote; }
+ }
+
+ ///
+ /// The time signature
+ ///
+ public string TimeSignature
+ {
+ get
+ {
+ string den = String.Format("Unknown ({0})",denominator);
+ switch(denominator)
+ {
+ case 1:
+ den = "2";
+ break;
+ case 2:
+ den = "4";
+ break;
+ case 3:
+ den = "8";
+ break;
+ case 4:
+ den = "16";
+ break;
+ case 5:
+ den = "32";
+ break;
+ }
+ return String.Format("{0}/{1}",numerator,den);
+ }
+ }
+
+ ///
+ /// Describes this time signature event
+ ///
+ /// A string describing this event
+ public override string ToString()
+ {
+ return String.Format("{0} {1} TicksInClick:{2} 32ndsInQuarterNote:{3}",
+ base.ToString(),TimeSignature,ticksInMetronomeClick,no32ndNotesInQuarterNote);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write(numerator);
+ writer.Write(denominator);
+ writer.Write(ticksInMetronomeClick);
+ writer.Write(no32ndNotesInQuarterNote);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Midi/TrackSequenceNumberEvent.cs b/NAudio/Midi/TrackSequenceNumberEvent.cs
new file mode 100644
index 00000000..98b9ebeb
--- /dev/null
+++ b/NAudio/Midi/TrackSequenceNumberEvent.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.Midi
+{
+ ///
+ /// Represents a MIDI track sequence number event event
+ ///
+ public class TrackSequenceNumberEvent : MetaEvent
+ {
+ private ushort sequenceNumber;
+
+ ///
+ /// Reads a new track sequence number event from a MIDI stream
+ ///
+ /// The MIDI stream
+ /// the data length
+ public TrackSequenceNumberEvent(BinaryReader br, int length)
+ {
+ // TODO: there is a form of the TrackSequenceNumberEvent that
+ // has a length of zero
+ if(length != 2)
+ {
+ throw new FormatException("Invalid sequence number length");
+ }
+ sequenceNumber = (ushort) ((br.ReadByte() << 8) + br.ReadByte());
+ }
+
+ ///
+ /// Describes this event
+ ///
+ /// String describing the event
+ public override string ToString()
+ {
+ return String.Format("{0} {1}", base.ToString(), sequenceNumber);
+ }
+
+ ///
+ /// Calls base class export first, then exports the data
+ /// specific to this event
+ /// MidiEvent.Export
+ ///
+ public override void Export(ref long absoluteTime, BinaryWriter writer)
+ {
+ base.Export(ref absoluteTime, writer);
+ writer.Write((byte)((sequenceNumber >> 8) & 0xFF));
+ writer.Write((byte)(sequenceNumber & 0xFF));
+ }
+ }
+}
diff --git a/NAudio/Mixer/BooleanMixerControl.cs b/NAudio/Mixer/BooleanMixerControl.cs
new file mode 100644
index 00000000..8336372d
--- /dev/null
+++ b/NAudio/Mixer/BooleanMixerControl.cs
@@ -0,0 +1,55 @@
+// created on 10/12/2002 at 23:58
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Boolean mixer control
+ ///
+ public class BooleanMixerControl : MixerControl
+ {
+ private MixerInterop.MIXERCONTROLDETAILS_BOOLEAN boolDetails;
+
+ internal BooleanMixerControl(MixerInterop.MIXERCONTROL mixerControl, IntPtr mixerHandle, MixerFlags mixerHandleType, int nChannels)
+ {
+ this.mixerControl = mixerControl;
+ this.mixerHandle = mixerHandle;
+ this.mixerHandleType = mixerHandleType;
+ this.nChannels = nChannels;
+ this.mixerControlDetails = new MixerInterop.MIXERCONTROLDETAILS();
+
+ GetControlDetails();
+
+ }
+
+ ///
+ /// Gets the details for this control
+ ///
+ /// memory pointer
+ protected override void GetDetails(IntPtr pDetails)
+ {
+ boolDetails = (MixerInterop.MIXERCONTROLDETAILS_BOOLEAN) Marshal.PtrToStructure(pDetails,typeof(MixerInterop.MIXERCONTROLDETAILS_BOOLEAN));
+ }
+
+ ///
+ /// The current value of the control
+ ///
+ public bool Value
+ {
+ get
+ {
+ GetControlDetails(); // make sure we have the latest value
+ return (boolDetails.fValue == 1);
+ }
+ set
+ {
+ boolDetails.fValue = (value) ? 1 : 0;
+ mixerControlDetails.paDetails = Marshal.AllocHGlobal(Marshal.SizeOf(boolDetails));
+ Marshal.StructureToPtr(boolDetails, mixerControlDetails.paDetails, false);
+ MmException.Try(MixerInterop.mixerSetControlDetails(mixerHandle, ref mixerControlDetails, MixerFlags.Value | mixerHandleType), "mixerSetControlDetails");
+ Marshal.FreeHGlobal(mixerControlDetails.paDetails);
+ }
+ }
+ }
+}
diff --git a/NAudio/Mixer/CustomMixerControl.cs b/NAudio/Mixer/CustomMixerControl.cs
new file mode 100644
index 00000000..b2b049c4
--- /dev/null
+++ b/NAudio/Mixer/CustomMixerControl.cs
@@ -0,0 +1,32 @@
+// created on 13/12/2002 at 22:07
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Custom Mixer control
+ ///
+ public class CustomMixerControl : MixerControl
+ {
+ internal CustomMixerControl(MixerInterop.MIXERCONTROL mixerControl, IntPtr mixerHandle, MixerFlags mixerHandleType, int nChannels)
+ {
+ this.mixerControl = mixerControl;
+ this.mixerHandle = mixerHandle;
+ this.mixerHandleType = mixerHandleType;
+ this.nChannels = nChannels;
+ this.mixerControlDetails = new MixerInterop.MIXERCONTROLDETAILS();
+ GetControlDetails();
+ }
+
+ ///
+ /// Get the data for this custom control
+ ///
+ /// pointer to memory to receive data
+ protected override void GetDetails(IntPtr pDetails)
+ {
+ }
+
+ // TODO: provide a way of getting / setting data
+ }
+}
diff --git a/NAudio/Mixer/ListTextMixerControl.cs b/NAudio/Mixer/ListTextMixerControl.cs
new file mode 100644
index 00000000..3efb53e3
--- /dev/null
+++ b/NAudio/Mixer/ListTextMixerControl.cs
@@ -0,0 +1,34 @@
+// created on 13/12/2002 at 22:06
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// List text mixer control
+ ///
+ public class ListTextMixerControl : MixerControl
+ {
+ internal ListTextMixerControl(MixerInterop.MIXERCONTROL mixerControl, IntPtr mixerHandle, MixerFlags mixerHandleType, int nChannels)
+ {
+ this.mixerControl = mixerControl;
+ this.mixerHandle = mixerHandle;
+ this.mixerHandleType = mixerHandleType;
+ this.nChannels = nChannels;
+ this.mixerControlDetails = new MixerInterop.MIXERCONTROLDETAILS();
+
+ GetControlDetails();
+
+ }
+
+ ///
+ /// Get the details for this control
+ ///
+ /// Memory location to read to
+ protected override void GetDetails(IntPtr pDetails)
+ {
+ }
+
+ // TODO: provide a way of getting / setting data
+ }
+}
diff --git a/NAudio/Mixer/Mixer.cs b/NAudio/Mixer/Mixer.cs
new file mode 100644
index 00000000..e3d386a6
--- /dev/null
+++ b/NAudio/Mixer/Mixer.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+using NAudio.Wave;
+
+namespace NAudio.Mixer
+{
+ /// Represents a Windows mixer device
+ public class Mixer
+ {
+ private MixerInterop.MIXERCAPS caps;
+ private IntPtr mixerHandle;
+ private MixerFlags mixerHandleType;
+
+ /// The number of mixer devices available
+ public static int NumberOfDevices
+ {
+ get
+ {
+ return MixerInterop.mixerGetNumDevs();
+ }
+ }
+
+ /// Connects to the specified mixer
+ /// The index of the mixer to use.
+ /// This should be between zero and NumberOfDevices - 1
+ public Mixer(int mixerIndex)
+ {
+ if(mixerIndex < 0 || mixerIndex >= NumberOfDevices)
+ {
+ throw new ArgumentOutOfRangeException("mixerID");
+ }
+ caps = new MixerInterop.MIXERCAPS();
+ MmException.Try(MixerInterop.mixerGetDevCaps((IntPtr)mixerIndex,ref caps,Marshal.SizeOf(caps)),"mixerGetDevCaps");
+ this.mixerHandle = (IntPtr)mixerIndex;
+ this.mixerHandleType = MixerFlags.Mixer;
+
+ // TODO: optionally support really opening the mixer device
+ //MmException.Try(MixerInterop.mixerOpen(out mixerHandle, mixerIndex, IntPtr.Zero, IntPtr.Zero, 0), "mixerOpen");
+ }
+
+ /// The number of destinations this mixer supports
+ public int DestinationCount
+ {
+ get
+ {
+ return (int) caps.cDestinations;
+ }
+ }
+
+ /// The name of this mixer device
+ public String Name
+ {
+ get
+ {
+ return caps.szPname;
+ }
+ }
+
+ /// The manufacturer code for this mixer device
+ public Manufacturers Manufacturer
+ {
+ get
+ {
+ return (Manufacturers) caps.wMid;
+ }
+ }
+
+ /// The product identifier code for this mixer device
+ public int ProductID
+ {
+ get
+ {
+ return caps.wPid;
+ }
+ }
+
+ /// Retrieve the specified MixerDestination object
+ /// The ID of the destination to use.
+ /// Should be between 0 and DestinationCount - 1
+ public MixerLine GetDestination(int destinationIndex)
+ {
+ if(destinationIndex < 0 || destinationIndex >= DestinationCount)
+ {
+ throw new ArgumentOutOfRangeException("destinationIndex");
+ }
+ return new MixerLine(mixerHandle, destinationIndex, mixerHandleType);
+ }
+
+ ///
+ /// A way to enumerate the destinations
+ ///
+ public IEnumerable Destinations
+ {
+ get
+ {
+ for (int destination = 0; destination < DestinationCount; destination++)
+ {
+ yield return GetDestination(destination);
+ }
+ }
+ }
+
+ ///
+ /// A way to enumerate all available devices
+ ///
+ public static IEnumerable Mixers
+ {
+ get
+ {
+ for (int device = 0; device < Mixer.NumberOfDevices; device++)
+ {
+ yield return new Mixer(device);
+ }
+ }
+ }
+ }
+}
diff --git a/NAudio/Mixer/MixerControl.cs b/NAudio/Mixer/MixerControl.cs
new file mode 100644
index 00000000..1860a115
--- /dev/null
+++ b/NAudio/Mixer/MixerControl.cs
@@ -0,0 +1,377 @@
+// created on 10/12/2002 at 21:11
+using System;
+using System.Runtime.InteropServices;
+using System.Collections.Generic;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Represents a mixer control
+ ///
+ public abstract class MixerControl
+ {
+ internal MixerInterop.MIXERCONTROL mixerControl;
+ internal MixerInterop.MIXERCONTROLDETAILS mixerControlDetails;
+
+ ///
+ /// Mixer Handle
+ ///
+ protected IntPtr mixerHandle;
+
+ ///
+ /// Number of Channels
+ ///
+ protected int nChannels;
+
+ ///
+ /// Mixer Handle Type
+ ///
+ protected MixerFlags mixerHandleType;
+
+ ///
+ /// Gets all the mixer controls
+ ///
+ /// Mixer Handle
+ /// Mixer Line
+ /// Mixer Handle Type
+ ///
+ public static IList GetMixerControls(IntPtr mixerHandle, MixerLine mixerLine,
+ MixerFlags mixerHandleType)
+ {
+ var controls = new List();
+ if (mixerLine.ControlsCount > 0)
+ {
+ int mixerControlSize = Marshal.SizeOf(typeof (MixerInterop.MIXERCONTROL));
+ var mlc = new MixerInterop.MIXERLINECONTROLS();
+ IntPtr pmc = Marshal.AllocHGlobal(mixerControlSize * mixerLine.ControlsCount);
+ mlc.cbStruct = Marshal.SizeOf(mlc);
+ mlc.dwLineID = mixerLine.LineId;
+ mlc.cControls = mixerLine.ControlsCount;
+ mlc.pamxctrl = pmc;
+ mlc.cbmxctrl = Marshal.SizeOf(typeof(MixerInterop.MIXERCONTROL));
+ try
+ {
+ MmResult err = MixerInterop.mixerGetLineControls(mixerHandle, ref mlc, MixerFlags.All | mixerHandleType);
+ if (err != MmResult.NoError)
+ {
+ throw new MmException(err, "mixerGetLineControls");
+ }
+ for (int i = 0; i < mlc.cControls; i++)
+ {
+ Int64 address = pmc.ToInt64() + mixerControlSize * i;
+
+ var mc = (MixerInterop.MIXERCONTROL)
+ Marshal.PtrToStructure((IntPtr)address, typeof(MixerInterop.MIXERCONTROL));
+ var mixerControl = GetMixerControl(mixerHandle, mixerLine.LineId, mc.dwControlID, mixerLine.Channels,
+ mixerHandleType);
+
+ controls.Add(mixerControl);
+ }
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(pmc);
+ }
+
+ }
+ return controls;
+ }
+
+ ///
+ /// Gets a specified Mixer Control
+ ///
+ /// Mixer Handle
+ /// Line ID
+ /// Control ID
+ /// Number of Channels
+ /// Flags to use (indicates the meaning of mixerHandle)
+ ///
+ public static MixerControl GetMixerControl(IntPtr mixerHandle, int nLineID, int controlId, int nChannels,
+ MixerFlags mixerFlags)
+ {
+ MixerInterop.MIXERLINECONTROLS mlc = new MixerInterop.MIXERLINECONTROLS();
+ MixerInterop.MIXERCONTROL mc = new MixerInterop.MIXERCONTROL();
+
+ // set up the pointer to a structure
+ IntPtr pMixerControl = Marshal.AllocCoTaskMem(Marshal.SizeOf(mc));
+ //Marshal.StructureToPtr(mc, pMixerControl, false);
+
+ mlc.cbStruct = Marshal.SizeOf(mlc);
+ mlc.cControls = 1;
+ mlc.dwControlID = controlId;
+ mlc.cbmxctrl = Marshal.SizeOf(mc);
+ mlc.pamxctrl = pMixerControl;
+ mlc.dwLineID = nLineID;
+ MmResult err = MixerInterop.mixerGetLineControls(mixerHandle, ref mlc, MixerFlags.OneById | mixerFlags);
+ if (err != MmResult.NoError)
+ {
+ Marshal.FreeCoTaskMem(pMixerControl);
+ throw new MmException(err, "mixerGetLineControls");
+ }
+
+ // retrieve the structure from the pointer
+ mc = (MixerInterop.MIXERCONTROL) Marshal.PtrToStructure(mlc.pamxctrl, typeof (MixerInterop.MIXERCONTROL));
+ Marshal.FreeCoTaskMem(pMixerControl);
+
+ if (MixerControl.IsControlBoolean(mc.dwControlType))
+ {
+ return new BooleanMixerControl(mc, mixerHandle, mixerFlags, nChannels);
+ }
+ else if (MixerControl.IsControlSigned(mc.dwControlType))
+ {
+ return new SignedMixerControl(mc, mixerHandle, mixerFlags, nChannels);
+ }
+ else if (MixerControl.IsControlUnsigned(mc.dwControlType))
+ {
+ return new UnsignedMixerControl(mc, mixerHandle, mixerFlags, nChannels);
+ }
+ else if (MixerControl.IsControlListText(mc.dwControlType))
+ {
+ return new ListTextMixerControl(mc, mixerHandle, mixerFlags, nChannels);
+ }
+ else if (MixerControl.IsControlCustom(mc.dwControlType))
+ {
+ return new CustomMixerControl(mc, mixerHandle, mixerFlags, nChannels);
+ }
+ else
+ {
+ throw new InvalidOperationException(String.Format("Unknown mixer control type {0}", mc.dwControlType));
+ }
+ }
+
+ ///
+ /// Gets the control details
+ ///
+ protected void GetControlDetails()
+ {
+ mixerControlDetails.cbStruct = Marshal.SizeOf(mixerControlDetails);
+ mixerControlDetails.dwControlID = mixerControl.dwControlID;
+ if (IsCustom)
+ {
+ mixerControlDetails.cChannels = 0;
+ }
+ else if ((mixerControl.fdwControl & MixerInterop.MIXERCONTROL_CONTROLF_UNIFORM) != 0)
+ {
+ mixerControlDetails.cChannels = 1;
+ }
+ else
+ {
+ mixerControlDetails.cChannels = nChannels;
+ }
+
+
+ if ((mixerControl.fdwControl & MixerInterop.MIXERCONTROL_CONTROLF_MULTIPLE) != 0)
+ {
+ mixerControlDetails.hwndOwner = (IntPtr) mixerControl.cMultipleItems;
+ }
+ else if (IsCustom)
+ {
+ mixerControlDetails.hwndOwner = IntPtr.Zero; // TODO: special cases
+ }
+ else
+ {
+ mixerControlDetails.hwndOwner = IntPtr.Zero;
+ }
+
+ if (IsBoolean)
+ {
+ mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_BOOLEAN());
+ }
+ else if (IsListText)
+ {
+ mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_LISTTEXT());
+ }
+ else if (IsSigned)
+ {
+ mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_SIGNED());
+ }
+ else if (IsUnsigned)
+ {
+ mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_UNSIGNED());
+ }
+ else
+ {
+ // must be custom
+ mixerControlDetails.cbDetails = mixerControl.Metrics.customData;
+ }
+ var detailsSize = mixerControlDetails.cbDetails*mixerControlDetails.cChannels;
+ if ((mixerControl.fdwControl & MixerInterop.MIXERCONTROL_CONTROLF_MULTIPLE) != 0)
+ {
+ // fixing issue 16390 - calculating size correctly for multiple items
+ detailsSize *= (int) mixerControl.cMultipleItems;
+ }
+ IntPtr buffer = Marshal.AllocCoTaskMem(detailsSize);
+ // To copy stuff in:
+ // Marshal.StructureToPtr( theStruct, buffer, false );
+ mixerControlDetails.paDetails = buffer;
+ MmResult err = MixerInterop.mixerGetControlDetails(mixerHandle, ref mixerControlDetails,
+ MixerFlags.Value | mixerHandleType);
+ // let the derived classes get the details before we free the handle
+ if (err == MmResult.NoError)
+ {
+ GetDetails(mixerControlDetails.paDetails);
+ }
+ Marshal.FreeCoTaskMem(buffer);
+ if (err != MmResult.NoError)
+ {
+ throw new MmException(err, "mixerGetControlDetails");
+ }
+ }
+
+ ///
+ /// Gets the control details
+ ///
+ ///
+ protected abstract void GetDetails(IntPtr pDetails);
+
+ ///
+ /// Mixer control name
+ ///
+ public String Name
+ {
+ get { return mixerControl.szName; }
+ }
+
+ ///
+ /// Mixer control type
+ ///
+ public MixerControlType ControlType
+ {
+ get { return mixerControl.dwControlType; }
+ }
+
+ ///
+ /// Returns true if this is a boolean control
+ ///
+ /// Control type
+ private static bool IsControlBoolean(MixerControlType controlType)
+ {
+ switch (controlType)
+ {
+ case MixerControlType.BooleanMeter:
+ case MixerControlType.Boolean:
+ case MixerControlType.Button:
+ case MixerControlType.Loudness:
+ case MixerControlType.Mono:
+ case MixerControlType.Mute:
+ case MixerControlType.OnOff:
+ case MixerControlType.StereoEnhance:
+ case MixerControlType.Mixer:
+ case MixerControlType.MultipleSelect:
+ case MixerControlType.Mux:
+ case MixerControlType.SingleSelect:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// Is this a boolean control
+ ///
+ public bool IsBoolean
+ {
+ get { return MixerControl.IsControlBoolean(mixerControl.dwControlType); }
+ }
+
+ ///
+ /// Determines whether a specified mixer control type is a list text control
+ ///
+ private static bool IsControlListText(MixerControlType controlType)
+ {
+ switch (controlType)
+ {
+ case MixerControlType.Equalizer:
+ case MixerControlType.Mixer:
+ case MixerControlType.MultipleSelect:
+ case MixerControlType.Mux:
+ case MixerControlType.SingleSelect:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// True if this is a list text control
+ ///
+ public bool IsListText
+ {
+ get { return MixerControl.IsControlListText(mixerControl.dwControlType); }
+ }
+
+ private static bool IsControlSigned(MixerControlType controlType)
+ {
+ switch (controlType)
+ {
+ case MixerControlType.PeakMeter:
+ case MixerControlType.SignedMeter:
+ case MixerControlType.Signed:
+ case MixerControlType.Decibels:
+ case MixerControlType.Pan:
+ case MixerControlType.QSoundPan:
+ case MixerControlType.Slider:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// True if this is a signed control
+ ///
+ public bool IsSigned
+ {
+ get { return MixerControl.IsControlSigned(mixerControl.dwControlType); }
+ }
+
+ private static bool IsControlUnsigned(MixerControlType controlType)
+ {
+ switch (controlType)
+ {
+ case MixerControlType.UnsignedMeter:
+ case MixerControlType.Unsigned:
+ case MixerControlType.Bass:
+ case MixerControlType.Equalizer:
+ case MixerControlType.Fader:
+ case MixerControlType.Treble:
+ case MixerControlType.Volume:
+ case MixerControlType.MicroTime:
+ case MixerControlType.MilliTime:
+ case MixerControlType.Percent:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ ///
+ /// True if this is an unsigned control
+ ///
+ public bool IsUnsigned
+ {
+ get { return MixerControl.IsControlUnsigned(mixerControl.dwControlType); }
+ }
+
+ private static bool IsControlCustom(MixerControlType controlType)
+ {
+ return (controlType == MixerControlType.Custom);
+ }
+
+ ///
+ /// True if this is a custom control
+ ///
+ public bool IsCustom
+ {
+ get { return MixerControl.IsControlCustom(mixerControl.dwControlType); }
+ }
+
+ ///
+ /// String representation for debug purposes
+ ///
+ public override string ToString()
+ {
+ return String.Format("{0} {1}", Name, ControlType);
+ }
+ }
+}
diff --git a/NAudio/Mixer/MixerControlType.cs b/NAudio/Mixer/MixerControlType.cs
new file mode 100644
index 00000000..795e85a2
--- /dev/null
+++ b/NAudio/Mixer/MixerControlType.cs
@@ -0,0 +1,111 @@
+// created on 15/12/2002 at 02:55
+using System;
+
+namespace NAudio.Mixer
+{
+ [Flags]
+ internal enum MixerControlClass
+ {
+ Custom = 0x00000000,
+ Meter = 0x10000000,
+ Switch = 0x20000000,
+ Number = 0x30000000,
+ Slider = 0x40000000,
+ Fader = 0x50000000,
+ Time = 0x60000000,
+ List = 0x70000000,
+ Mask = Custom | Meter | Switch | Number | Slider | Fader | Time | List
+ }
+
+ [Flags]
+ internal enum MixerControlSubclass
+ {
+ SwitchBoolean = 0x00000000,
+ SwitchButton = 0x01000000,
+ MeterPolled = 0x00000000,
+ TimeMicrosecs = 0x00000000,
+ TimeMillisecs = 0x01000000,
+ ListSingle = 0x00000000,
+ ListMultiple = 0x01000000,
+ Mask = 0x0F000000
+ }
+
+ [Flags]
+ internal enum MixerControlUnits
+ {
+ Custom = 0x00000000,
+ Boolean = 0x00010000,
+ Signed = 0x00020000,
+ Unsigned = 0x00030000,
+ Decibels = 0x00040000, // in 10ths
+ Percent = 0x00050000, // in 10ths
+ Mask = 0x00FF0000
+ }
+
+ ///
+ /// Mixer control types
+ ///
+ public enum MixerControlType
+ {
+ /// Custom
+ Custom = (MixerControlClass.Custom | MixerControlUnits.Custom),
+ /// Boolean meter
+ BooleanMeter = (MixerControlClass.Meter | MixerControlSubclass.MeterPolled | MixerControlUnits.Boolean),
+ /// Signed meter
+ SignedMeter = (MixerControlClass.Meter | MixerControlSubclass.MeterPolled | MixerControlUnits.Signed),
+ /// Peak meter
+ PeakMeter = (SignedMeter + 1),
+ /// Unsigned meter
+ UnsignedMeter = (MixerControlClass.Meter | MixerControlSubclass.MeterPolled | MixerControlUnits.Unsigned),
+ /// Boolean
+ Boolean = (MixerControlClass.Switch | MixerControlSubclass.SwitchBoolean | MixerControlUnits.Boolean),
+ /// On Off
+ OnOff = (Boolean + 1),
+ /// Mute
+ Mute = (Boolean + 2),
+ /// Mono
+ Mono = (Boolean + 3),
+ /// Loudness
+ Loudness = (Boolean + 4),
+ /// Stereo Enhance
+ StereoEnhance = (Boolean + 5),
+ /// Button
+ Button = (MixerControlClass.Switch | MixerControlSubclass.SwitchButton | MixerControlUnits.Boolean),
+ /// Decibels
+ Decibels = (MixerControlClass.Number | MixerControlUnits.Decibels),
+ /// Signed
+ Signed = (MixerControlClass.Number | MixerControlUnits.Signed),
+ /// Unsigned
+ Unsigned = (MixerControlClass.Number | MixerControlUnits.Unsigned),
+ /// Percent
+ Percent = (MixerControlClass.Number | MixerControlUnits.Percent),
+ /// Slider
+ Slider = (MixerControlClass.Slider | MixerControlUnits.Signed),
+ /// Pan
+ Pan = (Slider + 1),
+ /// Q-sound pan
+ QSoundPan = (Slider + 2),
+ /// Fader
+ Fader = (MixerControlClass.Fader | MixerControlUnits.Unsigned),
+ /// Volume
+ Volume = (Fader + 1),
+ /// Bass
+ Bass = (Fader + 2),
+ /// Treble
+ Treble = (Fader + 3),
+ /// Equaliser
+ Equalizer = (Fader + 4),
+ /// Single Select
+ SingleSelect = (MixerControlClass.List | MixerControlSubclass.ListSingle | MixerControlUnits.Boolean),
+ /// Mux
+ Mux = (SingleSelect + 1),
+ /// Multiple select
+ MultipleSelect = (MixerControlClass.List | MixerControlSubclass.ListMultiple | MixerControlUnits.Boolean),
+ /// Mixer
+ Mixer = (MultipleSelect + 1),
+ /// Micro time
+ MicroTime = (MixerControlClass.Time | MixerControlSubclass.TimeMicrosecs | MixerControlUnits.Unsigned),
+ /// Milli time
+ MilliTime = (MixerControlClass.Time | MixerControlSubclass.TimeMillisecs | MixerControlUnits.Unsigned),
+ }
+}
diff --git a/NAudio/Mixer/MixerFlags.cs b/NAudio/Mixer/MixerFlags.cs
new file mode 100644
index 00000000..1313d597
--- /dev/null
+++ b/NAudio/Mixer/MixerFlags.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Mixer Interop Flags
+ ///
+ [Flags]
+ public enum MixerFlags
+ {
+ #region Objects
+ ///
+ /// MIXER_OBJECTF_HANDLE = 0x80000000;
+ ///
+ Handle = unchecked ( (int) 0x80000000 ),
+ ///
+ /// MIXER_OBJECTF_MIXER = 0x00000000;
+ ///
+ Mixer = 0,
+ ///
+ /// MIXER_OBJECTF_HMIXER
+ ///
+ MixerHandle = Mixer | Handle,
+ ///
+ /// MIXER_OBJECTF_WAVEOUT
+ ///
+ WaveOut = 0x10000000,
+ ///
+ /// MIXER_OBJECTF_HWAVEOUT
+ ///
+ WaveOutHandle = WaveOut | Handle,
+ ///
+ /// MIXER_OBJECTF_WAVEIN
+ ///
+ WaveIn = 0x20000000,
+ ///
+ /// MIXER_OBJECTF_HWAVEIN
+ ///
+ WaveInHandle = WaveIn | Handle,
+ ///
+ /// MIXER_OBJECTF_MIDIOUT
+ ///
+ MidiOut = 0x30000000,
+ ///
+ /// MIXER_OBJECTF_HMIDIOUT
+ ///
+ MidiOutHandle = MidiOut | Handle,
+ ///
+ /// MIXER_OBJECTF_MIDIIN
+ ///
+ MidiIn = 0x40000000,
+ ///
+ /// MIXER_OBJECTF_HMIDIIN
+ ///
+ MidiInHandle = MidiIn | Handle,
+ ///
+ /// MIXER_OBJECTF_AUX
+ ///
+ Aux = 0x50000000,
+ #endregion
+
+ #region Get/Set control details
+ ///
+ /// MIXER_GETCONTROLDETAILSF_VALUE = 0x00000000;
+ /// MIXER_SETCONTROLDETAILSF_VALUE = 0x00000000;
+ ///
+ Value = 0,
+ ///
+ /// MIXER_GETCONTROLDETAILSF_LISTTEXT = 0x00000001;
+ /// MIXER_SETCONTROLDETAILSF_LISTTEXT = 0x00000001;
+ ///
+ ListText = 1,
+ ///
+ /// MIXER_GETCONTROLDETAILSF_QUERYMASK = 0x0000000F;
+ /// MIXER_SETCONTROLDETAILSF_QUERYMASK = 0x0000000F;
+ /// MIXER_GETLINECONTROLSF_QUERYMASK = 0x0000000F;
+ ///
+ QueryMask = 0xF,
+ #endregion
+
+ #region get line controls
+ ///
+ /// MIXER_GETLINECONTROLSF_ALL = 0x00000000;
+ ///
+ All = 0,
+ ///
+ /// MIXER_GETLINECONTROLSF_ONEBYID = 0x00000001;
+ ///
+ OneById = 1,
+ ///
+ /// MIXER_GETLINECONTROLSF_ONEBYTYPE = 0x00000002;
+ ///
+ OneByType = 2,
+ #endregion
+
+ ///
+ /// MIXER_GETLINEINFOF_DESTINATION = 0x00000000;
+ ///
+ GetLineInfoOfDestination = 0,
+ ///
+ /// MIXER_GETLINEINFOF_SOURCE = 0x00000001;
+ ///
+ GetLineInfoOfSource = 1,
+ ///
+ /// MIXER_GETLINEINFOF_LINEID = 0x00000002;
+ ///
+ GetLineInfoOfLineId = 2,
+ ///
+ /// MIXER_GETLINEINFOF_COMPONENTTYPE = 0x00000003;
+ ///
+ GetLineInfoOfComponentType = 3,
+ ///
+ /// MIXER_GETLINEINFOF_TARGETTYPE = 0x00000004;
+ ///
+ GetLineInfoOfTargetType = 4,
+ ///
+ /// MIXER_GETLINEINFOF_QUERYMASK = 0x0000000F;
+ ///
+ GetLineInfoOfQueryMask = 0xF,
+ }
+}
diff --git a/NAudio/Mixer/MixerInterop.cs b/NAudio/Mixer/MixerInterop.cs
new file mode 100644
index 00000000..dead1a00
--- /dev/null
+++ b/NAudio/Mixer/MixerInterop.cs
@@ -0,0 +1,267 @@
+// created on 09/12/2002 at 21:03
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+// TODO: add function help from MSDN
+// TODO: Create enums for flags parameters
+namespace NAudio.Mixer
+{
+ class MixerInterop
+ {
+ public const UInt32 MIXERCONTROL_CONTROLF_UNIFORM = 0x00000001;
+ public const UInt32 MIXERCONTROL_CONTROLF_MULTIPLE = 0x00000002;
+ public const UInt32 MIXERCONTROL_CONTROLF_DISABLED = 0x80000000;
+
+ public const Int32 MAXPNAMELEN = 32;
+ public const Int32 MIXER_SHORT_NAME_CHARS = 16;
+ public const Int32 MIXER_LONG_NAME_CHARS = 64;
+
+ // http://msdn.microsoft.com/en-us/library/dd757304%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern Int32 mixerGetNumDevs();
+
+ // http://msdn.microsoft.com/en-us/library/dd757308%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerOpen(out IntPtr hMixer, int uMxId, IntPtr dwCallback, IntPtr dwInstance, MixerFlags dwOpenFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd757292%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerClose(IntPtr hMixer);
+
+ // http://msdn.microsoft.com/en-us/library/dd757299%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerGetControlDetails(IntPtr hMixer, ref MIXERCONTROLDETAILS mixerControlDetails, MixerFlags dwDetailsFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd757300%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerGetDevCaps(IntPtr nMixerID, ref MIXERCAPS mixerCaps, Int32 mixerCapsSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd757301%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerGetID(IntPtr hMixer, out Int32 mixerID, MixerFlags dwMixerIDFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd757302%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerGetLineControls(IntPtr hMixer, ref MIXERLINECONTROLS mixerLineControls, MixerFlags dwControlFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd757303%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerGetLineInfo(IntPtr hMixer, ref MIXERLINE mixerLine, MixerFlags dwInfoFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd757307%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerMessage(IntPtr hMixer, UInt32 nMessage, IntPtr dwParam1, IntPtr dwParam2);
+
+ // http://msdn.microsoft.com/en-us/library/dd757309%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
+ public static extern MmResult mixerSetControlDetails(IntPtr hMixer, ref MIXERCONTROLDETAILS mixerControlDetails, MixerFlags dwDetailsFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd757294%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
+ public struct MIXERCONTROLDETAILS
+ {
+ public Int32 cbStruct; // size of the MIXERCONTROLDETAILS structure
+ public Int32 dwControlID;
+ public Int32 cChannels; // Number of channels on which to get or set control properties
+ public IntPtr hwndOwner; // Union with DWORD cMultipleItems
+ public Int32 cbDetails; // Size of the paDetails Member
+ public IntPtr paDetails; // LPVOID
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd757291%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ public struct MIXERCAPS
+ {
+ public UInt16 wMid;
+ public UInt16 wPid;
+ public UInt32 vDriverVersion; // MMVERSION - major high byte, minor low byte
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)]
+ public String szPname;
+ public UInt32 fdwSupport;
+ public UInt32 cDestinations;
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd757306%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ public struct MIXERLINECONTROLS
+ {
+ public Int32 cbStruct; // size of the MIXERLINECONTROLS structure
+ public Int32 dwLineID; // Line identifier for which controls are being queried
+ public Int32 dwControlID; // union with UInt32 dwControlType
+ public Int32 cControls;
+ public Int32 cbmxctrl;
+ public IntPtr pamxctrl; // see MSDN "Structs Sample"
+ }
+
+ ///
+ /// Mixer Line Flags
+ ///
+ [Flags]
+ public enum MIXERLINE_LINEF
+ {
+ ///
+ /// Audio line is active. An active line indicates that a signal is probably passing
+ /// through the line.
+ ///
+ MIXERLINE_LINEF_ACTIVE = 1,
+
+ ///
+ /// Audio line is disconnected. A disconnected line's associated controls can still be
+ /// modified, but the changes have no effect until the line is connected.
+ ///
+ MIXERLINE_LINEF_DISCONNECTED = 0x8000,
+
+ ///
+ /// Audio line is an audio source line associated with a single audio destination line.
+ /// If this flag is not set, this line is an audio destination line associated with zero
+ /// or more audio source lines.
+ ///
+ MIXERLINE_LINEF_SOURCE = (unchecked((int)0x80000000))
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd757305%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ public struct MIXERLINE
+ {
+ public Int32 cbStruct;
+ public Int32 dwDestination;
+ public Int32 dwSource;
+ public Int32 dwLineID;
+ public MIXERLINE_LINEF fdwLine;
+ public IntPtr dwUser;
+ public MixerLineComponentType dwComponentType;
+ public Int32 cChannels;
+ public Int32 cConnections;
+ public Int32 cControls;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_SHORT_NAME_CHARS)]
+ public String szShortName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_LONG_NAME_CHARS)]
+ public String szName;
+ // start of target struct 'Target'
+ public UInt32 dwType;
+ public UInt32 dwDeviceID;
+ public UInt16 wMid;
+ public UInt16 wPid;
+ public UInt32 vDriverVersion; // MMVERSION - major high byte, minor low byte
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)]
+ public String szPname;
+ // end of target struct
+ }
+
+ ///
+ /// BOUNDS structure
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ public struct Bounds
+ {
+ ///
+ /// dwMinimum / lMinimum / reserved 0
+ ///
+ public int minimum;
+ ///
+ /// dwMaximum / lMaximum / reserved 1
+ ///
+ public int maximum;
+ ///
+ /// reserved 2
+ ///
+ public int reserved2;
+ ///
+ /// reserved 3
+ ///
+ public int reserved3;
+ ///
+ /// reserved 4
+ ///
+ public int reserved4;
+ ///
+ /// reserved 5
+ ///
+ public int reserved5;
+ }
+
+ ///
+ /// METRICS structure
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ public struct Metrics
+ {
+ ///
+ /// cSteps / reserved[0]
+ ///
+ public int step;
+ ///
+ /// cbCustomData / reserved[1], number of bytes for control details
+ ///
+ public int customData;
+ ///
+ /// reserved 2
+ ///
+ public int reserved2;
+ ///
+ /// reserved 3
+ ///
+ public int reserved3;
+ ///
+ /// reserved 4
+ ///
+ public int reserved4;
+ ///
+ /// reserved 5
+ ///
+ public int reserved5;
+ }
+
+ ///
+ /// MIXERCONTROL struct
+ /// http://msdn.microsoft.com/en-us/library/dd757293%28VS.85%29.aspx
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ public struct MIXERCONTROL
+ {
+ public UInt32 cbStruct;
+ public Int32 dwControlID;
+ public MixerControlType dwControlType;
+ public UInt32 fdwControl;
+ public UInt32 cMultipleItems;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_SHORT_NAME_CHARS)]
+ public String szShortName;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_LONG_NAME_CHARS)]
+ public String szName;
+ public Bounds Bounds;
+ public Metrics Metrics;
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd757295%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct MIXERCONTROLDETAILS_BOOLEAN
+ {
+ public Int32 fValue;
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd757297%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct MIXERCONTROLDETAILS_SIGNED
+ {
+ public Int32 lValue;
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd757296%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
+ public struct MIXERCONTROLDETAILS_LISTTEXT
+ {
+ public UInt32 dwParam1;
+ public UInt32 dwParam2;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_LONG_NAME_CHARS)]
+ public String szName;
+ }
+
+ // http://msdn.microsoft.com/en-us/library/dd757298%28VS.85%29.aspx
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct MIXERCONTROLDETAILS_UNSIGNED
+ {
+ public UInt32 dwValue;
+ }
+ }
+}
diff --git a/NAudio/Mixer/MixerLine.cs b/NAudio/Mixer/MixerLine.cs
new file mode 100644
index 00000000..ee0da3c3
--- /dev/null
+++ b/NAudio/Mixer/MixerLine.cs
@@ -0,0 +1,288 @@
+// created on 10/12/2002 at 20:37
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+using System.Collections.Generic;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Represents a mixer line (source or destination)
+ ///
+ public class MixerLine
+ {
+ private MixerInterop.MIXERLINE mixerLine;
+ private IntPtr mixerHandle;
+ private MixerFlags mixerHandleType;
+
+ ///
+ /// Creates a new mixer destination
+ ///
+ /// Mixer Handle
+ /// Destination Index
+ /// Mixer Handle Type
+ public MixerLine(IntPtr mixerHandle, int destinationIndex, MixerFlags mixerHandleType)
+ {
+ this.mixerHandle = mixerHandle;
+ this.mixerHandleType = mixerHandleType;
+ mixerLine = new MixerInterop.MIXERLINE();
+ mixerLine.cbStruct = Marshal.SizeOf(mixerLine);
+ mixerLine.dwDestination = destinationIndex;
+ MmException.Try(MixerInterop.mixerGetLineInfo(mixerHandle, ref mixerLine, mixerHandleType | MixerFlags.GetLineInfoOfDestination), "mixerGetLineInfo");
+ }
+
+ ///
+ /// Creates a new Mixer Source For a Specified Source
+ ///
+ /// Mixer Handle
+ /// Destination Index
+ /// Source Index
+ /// Flag indicating the meaning of mixerHandle
+ public MixerLine(IntPtr mixerHandle, int destinationIndex, int sourceIndex, MixerFlags mixerHandleType)
+ {
+ this.mixerHandle = mixerHandle;
+ this.mixerHandleType = mixerHandleType;
+ mixerLine = new MixerInterop.MIXERLINE();
+ mixerLine.cbStruct = Marshal.SizeOf(mixerLine);
+ mixerLine.dwDestination = destinationIndex;
+ mixerLine.dwSource = sourceIndex;
+ MmException.Try(MixerInterop.mixerGetLineInfo(mixerHandle, ref mixerLine, mixerHandleType | MixerFlags.GetLineInfoOfSource), "mixerGetLineInfo");
+ }
+
+ ///
+ /// Creates a new Mixer Source
+ ///
+ /// Wave In Device
+ public static int GetMixerIdForWaveIn(int waveInDevice)
+ {
+ int mixerId = -1;
+ MmException.Try(MixerInterop.mixerGetID((IntPtr)waveInDevice, out mixerId, MixerFlags.WaveIn), "mixerGetID");
+ return mixerId;
+ }
+
+ ///
+ /// Mixer Line Name
+ ///
+ public String Name
+ {
+ get
+ {
+ return mixerLine.szName;
+ }
+ }
+
+ ///
+ /// Mixer Line short name
+ ///
+ public String ShortName
+ {
+ get
+ {
+ return mixerLine.szShortName;
+ }
+ }
+
+ ///
+ /// The line ID
+ ///
+ public int LineId
+ {
+ get
+ {
+ return mixerLine.dwLineID;
+ }
+ }
+
+ ///
+ /// Component Type
+ ///
+ public MixerLineComponentType ComponentType
+ {
+ get
+ {
+ return mixerLine.dwComponentType;
+ }
+ }
+
+ ///
+ /// Mixer destination type description
+ ///
+ public String TypeDescription
+ {
+ get
+ {
+ switch (mixerLine.dwComponentType)
+ {
+ // destinations
+ case MixerLineComponentType.DestinationUndefined:
+ return "Undefined Destination";
+ case MixerLineComponentType.DestinationDigital:
+ return "Digital Destination";
+ case MixerLineComponentType.DestinationLine:
+ return "Line Level Destination";
+ case MixerLineComponentType.DestinationMonitor:
+ return "Monitor Destination";
+ case MixerLineComponentType.DestinationSpeakers:
+ return "Speakers Destination";
+ case MixerLineComponentType.DestinationHeadphones:
+ return "Headphones Destination";
+ case MixerLineComponentType.DestinationTelephone:
+ return "Telephone Destination";
+ case MixerLineComponentType.DestinationWaveIn:
+ return "Wave Input Destination";
+ case MixerLineComponentType.DestinationVoiceIn:
+ return "Voice Recognition Destination";
+ // sources
+ case MixerLineComponentType.SourceUndefined:
+ return "Undefined Source";
+ case MixerLineComponentType.SourceDigital:
+ return "Digital Source";
+ case MixerLineComponentType.SourceLine:
+ return "Line Level Source";
+ case MixerLineComponentType.SourceMicrophone:
+ return "Microphone Source";
+ case MixerLineComponentType.SourceSynthesizer:
+ return "Synthesizer Source";
+ case MixerLineComponentType.SourceCompactDisc:
+ return "Compact Disk Source";
+ case MixerLineComponentType.SourceTelephone:
+ return "Telephone Source";
+ case MixerLineComponentType.SourcePcSpeaker:
+ return "PC Speaker Source";
+ case MixerLineComponentType.SourceWaveOut:
+ return "Wave Out Source";
+ case MixerLineComponentType.SourceAuxiliary:
+ return "Auxiliary Source";
+ case MixerLineComponentType.SourceAnalog:
+ return "Analog Source";
+ default:
+ return "Invalid Component Type";
+ }
+ }
+ }
+
+ ///
+ /// Number of channels
+ ///
+ public int Channels
+ {
+ get
+ {
+ return mixerLine.cChannels;
+ }
+ }
+
+ ///
+ /// Number of sources
+ ///
+ public int SourceCount
+ {
+ get
+ {
+ return mixerLine.cConnections;
+ }
+ }
+
+ ///
+ /// Number of controls
+ ///
+ public int ControlsCount
+ {
+ get
+ {
+ return mixerLine.cControls;
+ }
+ }
+
+ ///
+ /// Is this destination active
+ ///
+ public bool IsActive
+ {
+ get
+ {
+ return (mixerLine.fdwLine & MixerInterop.MIXERLINE_LINEF.MIXERLINE_LINEF_ACTIVE) != 0;
+ }
+ }
+
+ ///
+ /// Is this destination disconnected
+ ///
+ public bool IsDisconnected
+ {
+ get
+ {
+ return (mixerLine.fdwLine & MixerInterop.MIXERLINE_LINEF.MIXERLINE_LINEF_DISCONNECTED) != 0;
+ }
+ }
+
+ ///
+ /// Is this destination a source
+ ///
+ public bool IsSource
+ {
+ get
+ {
+ return (mixerLine.fdwLine & MixerInterop.MIXERLINE_LINEF.MIXERLINE_LINEF_SOURCE) != 0;
+ }
+ }
+
+ ///
+ /// Gets the specified source
+ ///
+ public MixerLine GetSource(int sourceIndex)
+ {
+ if(sourceIndex < 0 || sourceIndex >= SourceCount)
+ {
+ throw new ArgumentOutOfRangeException("sourceIndex");
+ }
+ return new MixerLine(mixerHandle, mixerLine.dwDestination, sourceIndex, this.mixerHandleType);
+ }
+
+ ///
+ /// Enumerator for the controls on this Mixer Limne
+ ///
+ public IEnumerable Controls
+ {
+ get
+ {
+ return MixerControl.GetMixerControls(this.mixerHandle, this, this.mixerHandleType);
+ }
+ }
+
+ ///
+ /// Enumerator for the sources on this Mixer Line
+ ///
+ public IEnumerable Sources
+ {
+ get
+ {
+ for (int source = 0; source < SourceCount; source++)
+ {
+ yield return GetSource(source);
+ }
+ }
+ }
+
+ ///
+ /// The name of the target output device
+ ///
+ public string TargetName
+ {
+ get
+ {
+ return mixerLine.szPname;
+ }
+ }
+
+ ///
+ /// Describes this Mixer Line (for diagnostic purposes)
+ ///
+ public override string ToString()
+ {
+ return String.Format("{0} {1} ({2} controls, ID={3})",
+ Name, TypeDescription, ControlsCount, mixerLine.dwLineID);
+ }
+ }
+}
+
diff --git a/NAudio/Mixer/MixerLineComponentType.cs b/NAudio/Mixer/MixerLineComponentType.cs
new file mode 100644
index 00000000..4edaecba
--- /dev/null
+++ b/NAudio/Mixer/MixerLineComponentType.cs
@@ -0,0 +1,114 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Mixer Line Component type enumeration
+ ///
+ public enum MixerLineComponentType
+ {
+ ///
+ /// Audio line is a destination that cannot be defined by one of the standard component types. A mixer device is required to use this component type for line component types that have not been defined by Microsoft Corporation.
+ /// MIXERLINE_COMPONENTTYPE_DST_UNDEFINED
+ ///
+ DestinationUndefined = 0,
+ ///
+ /// Audio line is a digital destination (for example, digital input to a DAT or CD audio device).
+ /// MIXERLINE_COMPONENTTYPE_DST_DIGITAL
+ ///
+ DestinationDigital = 1,
+ ///
+ /// Audio line is a line level destination (for example, line level input from a CD audio device) that will be the final recording source for the analog-to-digital converter (ADC). Because most audio cards for personal computers provide some sort of gain for the recording audio source line, the mixer device will use the MIXERLINE_COMPONENTTYPE_DST_WAVEIN type.
+ /// MIXERLINE_COMPONENTTYPE_DST_LINE
+ ///
+ DestinationLine = 2,
+ ///
+ /// Audio line is a destination used for a monitor.
+ /// MIXERLINE_COMPONENTTYPE_DST_MONITOR
+ ///
+ DestinationMonitor = 3,
+ ///
+ /// Audio line is an adjustable (gain and/or attenuation) destination intended to drive speakers. This is the typical component type for the audio output of audio cards for personal computers.
+ /// MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
+ ///
+ DestinationSpeakers = 4,
+ ///
+ /// Audio line is an adjustable (gain and/or attenuation) destination intended to drive headphones. Most audio cards use the same audio destination line for speakers and headphones, in which case the mixer device simply uses the MIXERLINE_COMPONENTTYPE_DST_SPEAKERS type.
+ /// MIXERLINE_COMPONENTTYPE_DST_HEADPHONES
+ ///
+ DestinationHeadphones = 5,
+ ///
+ /// Audio line is a destination that will be routed to a telephone line.
+ /// MIXERLINE_COMPONENTTYPE_DST_TELEPHONE
+ ///
+ DestinationTelephone = 6,
+ ///
+ /// Audio line is a destination that will be the final recording source for the waveform-audio input (ADC). This line typically provides some sort of gain or attenuation. This is the typical component type for the recording line of most audio cards for personal computers.
+ /// MIXERLINE_COMPONENTTYPE_DST_WAVEIN
+ ///
+ DestinationWaveIn = 7,
+ ///
+ /// Audio line is a destination that will be the final recording source for voice input. This component type is exactly like MIXERLINE_COMPONENTTYPE_DST_WAVEIN but is intended specifically for settings used during voice recording/recognition. Support for this line is optional for a mixer device. Many mixer devices provide only MIXERLINE_COMPONENTTYPE_DST_WAVEIN.
+ /// MIXERLINE_COMPONENTTYPE_DST_VOICEIN
+ ///
+ DestinationVoiceIn = 8,
+ ///
+ /// Audio line is a source that cannot be defined by one of the standard component types. A mixer device is required to use this component type for line component types that have not been defined by Microsoft Corporation.
+ /// MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED
+ ///
+ SourceUndefined = 0x1000,
+ ///
+ /// Audio line is a digital source (for example, digital output from a DAT or audio CD).
+ /// MIXERLINE_COMPONENTTYPE_SRC_DIGITAL
+ ///
+ SourceDigital = 0x1001,
+ ///
+ /// Audio line is a line-level source (for example, line-level input from an external stereo) that can be used as an optional recording source. Because most audio cards for personal computers provide some sort of gain for the recording source line, the mixer device will use the MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY type.
+ /// MIXERLINE_COMPONENTTYPE_SRC_LINE
+ ///
+ SourceLine = 0x1002,
+ ///
+ /// Audio line is a microphone recording source. Most audio cards for personal computers provide at least two types of recording sources: an auxiliary audio line and microphone input. A microphone audio line typically provides some sort of gain. Audio cards that use a single input for use with a microphone or auxiliary audio line should use the MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE component type.
+ /// MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE
+ ///
+ SourceMicrophone = 0x1003,
+ ///
+ /// Audio line is a source originating from the output of an internal synthesizer. Most audio cards for personal computers provide some sort of MIDI synthesizer (for example, an Adlib®-compatible or OPL/3 FM synthesizer).
+ /// MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER
+ ///
+ SourceSynthesizer = 0x1004,
+ ///
+ /// Audio line is a source originating from the output of an internal audio CD. This component type is provided for audio cards that provide an audio source line intended to be connected to an audio CD (or CD-ROM playing an audio CD).
+ /// MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC
+ ///
+ SourceCompactDisc = 0x1005,
+ ///
+ /// Audio line is a source originating from an incoming telephone line.
+ /// MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE
+ ///
+ SourceTelephone = 0x1006,
+ ///
+ /// Audio line is a source originating from personal computer speaker. Several audio cards for personal computers provide the ability to mix what would typically be played on the internal speaker with the output of an audio card. Some audio cards support the ability to use this output as a recording source.
+ /// MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER
+ ///
+ SourcePcSpeaker = 0x1007,
+ ///
+ /// Audio line is a source originating from the waveform-audio output digital-to-analog converter (DAC). Most audio cards for personal computers provide this component type as a source to the MIXERLINE_COMPONENTTYPE_DST_SPEAKERS destination. Some cards also allow this source to be routed to the MIXERLINE_COMPONENTTYPE_DST_WAVEIN destination.
+ /// MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT
+ ///
+ SourceWaveOut = 0x1008,
+ ///
+ /// Audio line is a source originating from the auxiliary audio line. This line type is intended as a source with gain or attenuation that can be routed to the MIXERLINE_COMPONENTTYPE_DST_SPEAKERS destination and/or recorded from the MIXERLINE_COMPONENTTYPE_DST_WAVEIN destination.
+ /// MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY
+ ///
+ SourceAuxiliary = 0x1009,
+ ///
+ /// Audio line is an analog source (for example, analog output from a video-cassette tape).
+ /// MIXERLINE_COMPONENTTYPE_SRC_ANALOG
+ ///
+ SourceAnalog = 0x100A,
+
+ }
+}
diff --git a/NAudio/Mixer/MixerSource.cs b/NAudio/Mixer/MixerSource.cs
new file mode 100644
index 00000000..83c46d3f
--- /dev/null
+++ b/NAudio/Mixer/MixerSource.cs
@@ -0,0 +1,131 @@
+// created on 10/12/2002 at 21:00
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Represents a Mixer source
+ ///
+ public class MixerSource
+ {
+ private MixerInterop.MIXERLINE mixerLine;
+ private IntPtr mixerHandle;
+ private int nDestination;
+ private int nSource;
+
+ ///
+ /// Creates a new Mixer Source
+ ///
+ /// Mixer ID
+ /// Destination ID
+ /// Source ID
+ public MixerSource(IntPtr mixerHandle, int nDestination, int nSource)
+ {
+ mixerLine = new MixerInterop.MIXERLINE();
+ mixerLine.cbStruct = Marshal.SizeOf(mixerLine);
+ mixerLine.dwDestination = nDestination;
+ mixerLine.dwSource = nSource;
+ MmException.Try(MixerInterop.mixerGetLineInfo(mixerHandle, ref mixerLine, MixerInterop.MIXER_GETLINEINFOF_SOURCE), "mixerGetLineInfo");
+ this.mixerHandle = mixerHandle;
+ this.nDestination = nDestination;
+ this.nSource = nSource;
+ }
+
+ ///
+ /// Source Name
+ ///
+ public String Name
+ {
+ get
+ {
+ return mixerLine.szName;
+ }
+ }
+
+ ///
+ /// Source short name
+ ///
+ public String ShortName
+ {
+ get
+ {
+ return mixerLine.szShortName;
+ }
+ }
+
+ ///
+ /// Number of controls
+ ///
+ public int ControlsCount
+ {
+ get
+ {
+ return mixerLine.cControls;
+ }
+ }
+
+ ///
+ /// Retrieves the specified control
+ ///
+ ///
+ ///
+ public MixerControl GetControl(int nControl)
+ {
+ if(nControl < 0 || nControl >= ControlsCount)
+ {
+ throw new ArgumentOutOfRangeException("nControl");
+ }
+ return MixerControl.GetMixerControl(mixerHandle, (int)mixerLine.dwLineID, nControl, Channels);
+ }
+
+ ///
+ /// Number of channels
+ ///
+ public int Channels
+ {
+ get
+ {
+ return mixerLine.cChannels;
+ }
+ }
+
+ ///
+ /// Source type description
+ ///
+ public String TypeDescription
+ {
+ get
+ {
+ switch (mixerLine.dwComponentType)
+ {
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED:
+ return "Undefined";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_DIGITAL:
+ return "Digital";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_LINE:
+ return "Line Level";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE:
+ return "Microphone";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER:
+ return "Synthesizer";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC:
+ return "Compact Disk";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE:
+ return "Telephone";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_PCSPEAKER:
+ return "PC Speaker";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT:
+ return "Wave Out";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY:
+ return "Auxiliary";
+ case MixerInterop.MIXERLINE_COMPONENTTYPE.MIXERLINE_COMPONENTTYPE_SRC_ANALOG:
+ return "Analog";
+ default:
+ return "Invalid";
+ }
+ }
+ }
+ }
+}
diff --git a/NAudio/Mixer/SignedMixerControl.cs b/NAudio/Mixer/SignedMixerControl.cs
new file mode 100644
index 00000000..e5f19eb0
--- /dev/null
+++ b/NAudio/Mixer/SignedMixerControl.cs
@@ -0,0 +1,99 @@
+// created on 13/12/2002 at 22:01
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Represents a signed mixer control
+ ///
+ public class SignedMixerControl : MixerControl
+ {
+ private MixerInterop.MIXERCONTROLDETAILS_SIGNED signedDetails;
+
+ internal SignedMixerControl(MixerInterop.MIXERCONTROL mixerControl, IntPtr mixerHandle, MixerFlags mixerHandleType, int nChannels)
+ {
+ this.mixerControl = mixerControl;
+ this.mixerHandle = mixerHandle;
+ this.mixerHandleType = mixerHandleType;
+ this.nChannels = nChannels;
+ this.mixerControlDetails = new MixerInterop.MIXERCONTROLDETAILS();
+ GetControlDetails();
+ }
+
+ ///
+ /// Gets details for this contrl
+ ///
+ protected override void GetDetails(IntPtr pDetails)
+ {
+ signedDetails = (MixerInterop.MIXERCONTROLDETAILS_SIGNED) Marshal.PtrToStructure(mixerControlDetails.paDetails,typeof(MixerInterop.MIXERCONTROLDETAILS_SIGNED));
+ }
+
+ ///
+ /// The value of the control
+ ///
+ public int Value
+ {
+ get
+ {
+ GetControlDetails();
+ return signedDetails.lValue;
+ }
+ set
+ {
+ signedDetails.lValue = value;
+ mixerControlDetails.paDetails = Marshal.AllocHGlobal(Marshal.SizeOf(signedDetails));
+ Marshal.StructureToPtr(signedDetails, mixerControlDetails.paDetails, false);
+ MmException.Try(MixerInterop.mixerSetControlDetails(mixerHandle, ref mixerControlDetails, MixerFlags.Value | mixerHandleType), "mixerSetControlDetails");
+ Marshal.FreeHGlobal(mixerControlDetails.paDetails);
+ }
+ }
+
+ ///
+ /// Minimum value for this control
+ ///
+ public int MinValue
+ {
+ get
+ {
+ return mixerControl.Bounds.minimum;
+ }
+ }
+
+ ///
+ /// Maximum value for this control
+ ///
+ public int MaxValue
+ {
+ get
+ {
+ return mixerControl.Bounds.maximum;
+ }
+ }
+
+ ///
+ /// Value of the control represented as a percentage
+ ///
+ public double Percent
+ {
+ get
+ {
+ return 100.0 * (Value - MinValue) / (double)(MaxValue - MinValue);
+ }
+ set
+ {
+ Value = (int)(MinValue + (value / 100.0) * (MaxValue - MinValue));
+ }
+ }
+
+ ///
+ /// String Representation for debugging purposes
+ ///
+ ///
+ public override string ToString()
+ {
+ return String.Format("{0} {1}%", base.ToString(), Percent);
+ }
+ }
+}
diff --git a/NAudio/Mixer/UnsignedMixerControl.cs b/NAudio/Mixer/UnsignedMixerControl.cs
new file mode 100644
index 00000000..e05506f7
--- /dev/null
+++ b/NAudio/Mixer/UnsignedMixerControl.cs
@@ -0,0 +1,108 @@
+// created on 13/12/2002 at 22:04
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudio.Mixer
+{
+ ///
+ /// Represents an unsigned mixer control
+ ///
+ public class UnsignedMixerControl : MixerControl
+ {
+ private MixerInterop.MIXERCONTROLDETAILS_UNSIGNED[] unsignedDetails;
+
+ internal UnsignedMixerControl(MixerInterop.MIXERCONTROL mixerControl,IntPtr mixerHandle, MixerFlags mixerHandleType, int nChannels)
+ {
+ this.mixerControl = mixerControl;
+ this.mixerHandle = mixerHandle;
+ this.mixerHandleType = mixerHandleType;
+ this.nChannels = nChannels;
+ this.mixerControlDetails = new MixerInterop.MIXERCONTROLDETAILS();
+ GetControlDetails();
+ }
+
+ ///
+ /// Gets the details for this control
+ ///
+ protected override void GetDetails(IntPtr pDetails)
+ {
+ unsignedDetails = new MixerInterop.MIXERCONTROLDETAILS_UNSIGNED[nChannels];
+ for (int channel = 0; channel < nChannels; channel++)
+ {
+ unsignedDetails[channel] = (MixerInterop.MIXERCONTROLDETAILS_UNSIGNED)Marshal.PtrToStructure(mixerControlDetails.paDetails, typeof(MixerInterop.MIXERCONTROLDETAILS_UNSIGNED));
+ }
+ }
+
+ ///
+ /// The control value
+ ///
+ public uint Value
+ {
+ get
+ {
+ GetControlDetails();
+ return unsignedDetails[0].dwValue;
+ }
+ set
+ {
+ int structSize = Marshal.SizeOf(unsignedDetails[0]);
+
+ mixerControlDetails.paDetails = Marshal.AllocHGlobal(structSize * nChannels);
+ for (int channel = 0; channel < nChannels; channel++)
+ {
+ unsignedDetails[channel].dwValue = value;
+ long pointer = mixerControlDetails.paDetails.ToInt64() + (structSize * channel);
+ Marshal.StructureToPtr(unsignedDetails[channel], (IntPtr)pointer, false);
+ }
+ MmException.Try(MixerInterop.mixerSetControlDetails(mixerHandle, ref mixerControlDetails, MixerFlags.Value | mixerHandleType), "mixerSetControlDetails");
+ Marshal.FreeHGlobal(mixerControlDetails.paDetails);
+ }
+ }
+
+ ///
+ /// The control's minimum value
+ ///
+ public UInt32 MinValue
+ {
+ get
+ {
+ return (uint) mixerControl.Bounds.minimum;
+ }
+ }
+
+ ///
+ /// The control's maximum value
+ ///
+ public UInt32 MaxValue
+ {
+ get
+ {
+ return (uint) mixerControl.Bounds.maximum;
+ }
+ }
+
+ ///
+ /// Value of the control represented as a percentage
+ ///
+ public double Percent
+ {
+ get
+ {
+ return 100.0 * (Value - MinValue) / (double)(MaxValue - MinValue);
+ }
+ set
+ {
+ Value = (uint)(MinValue + (value / 100.0) * (MaxValue - MinValue));
+ }
+ }
+
+ ///
+ /// String Representation for debugging purposes
+ ///
+ public override string ToString()
+ {
+ return String.Format("{0} {1}%", base.ToString(), Percent);
+ }
+ }
+}
diff --git a/NAudio/NAudio.csproj b/NAudio/NAudio.csproj
new file mode 100644
index 00000000..6b53dc36
--- /dev/null
+++ b/NAudio/NAudio.csproj
@@ -0,0 +1,551 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}
+ Library
+ Properties
+ NAudio
+ NAudio
+
+
+
+
+
+
+
+
+
+
+
+
+ 3.5
+ v3.5
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+ Client
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+ bin\Debug\NAudio.XML
+ AnyCPU
+ NAudio.ruleset
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+ AnyCPU
+ AllRules.ruleset
+ bin\Release\NAudio.XML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Component
+
+
+ UserControl
+
+
+ UserControl
+
+
+ Pot.cs
+
+
+ Component
+
+
+ VolumeMeter.cs
+
+
+ UserControl
+
+
+ Component
+
+
+ WaveformPainter.cs
+
+
+ UserControl
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ ProgressLog.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fader.cs
+ Designer
+
+
+ PanSlider.cs
+ Designer
+
+
+ Pot.cs
+ Designer
+
+
+ VolumeSlider.cs
+ Designer
+
+
+ WaveViewer.cs
+ Designer
+
+
+ ProgressLog.cs
+ Designer
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudio/NAudio.csproj.vspscc b/NAudio/NAudio.csproj.vspscc
new file mode 100644
index 00000000..feffdeca
--- /dev/null
+++ b/NAudio/NAudio.csproj.vspscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = ""
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
+}
diff --git a/NAudio/NAudio.ruleset b/NAudio/NAudio.ruleset
new file mode 100644
index 00000000..f4c759d4
--- /dev/null
+++ b/NAudio/NAudio.ruleset
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudio/Properties/AssemblyInfo.cs b/NAudio/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..9054c60a
--- /dev/null
+++ b/NAudio/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NAudio")]
+[assembly: AssemblyDescription("NAudio .NET Audio Library")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Mark Heath")]
+[assembly: AssemblyProduct("NAudio")]
+[assembly: AssemblyCopyright("© 2001-2014 Mark Heath")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: InternalsVisibleTo("NAudioTests")]
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("e82fa7f0-f952-4d93-b7b0-392bbf53b2a4")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.7.2.19")]
+[assembly: AssemblyFileVersion("1.7.2.19")]
diff --git a/NAudio/Utils/BufferHelpers.cs b/NAudio/Utils/BufferHelpers.cs
new file mode 100644
index 00000000..ea540335
--- /dev/null
+++ b/NAudio/Utils/BufferHelpers.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// Helper methods for working with audio buffers
+ ///
+ public static class BufferHelpers
+ {
+ ///
+ /// Ensures the buffer is big enough
+ ///
+ ///
+ ///
+ ///
+ public static byte[] Ensure(byte[] buffer, int bytesRequired)
+ {
+ if (buffer == null || buffer.Length < bytesRequired)
+ {
+ buffer = new byte[bytesRequired];
+ }
+ return buffer;
+ }
+
+ ///
+ /// Ensures the buffer is big enough
+ ///
+ ///
+ ///
+ ///
+ public static float[] Ensure(float[] buffer, int samplesRequired)
+ {
+ if (buffer == null || buffer.Length < samplesRequired)
+ {
+ buffer = new float[samplesRequired];
+ }
+ return buffer;
+ }
+ }
+}
diff --git a/NAudio/Utils/ByteArrayExtensions.cs b/NAudio/Utils/ByteArrayExtensions.cs
new file mode 100644
index 00000000..e5ce31c6
--- /dev/null
+++ b/NAudio/Utils/ByteArrayExtensions.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+
+
+namespace NAudio.Utils
+{
+ ///
+ /// these will become extension methods once we move to .NET 3.5
+ ///
+ public static class ByteArrayExtensions
+ {
+ ///
+ /// Checks if the buffer passed in is entirely full of nulls
+ ///
+ public static bool IsEntirelyNull(byte[] buffer)
+ {
+ foreach (byte b in buffer)
+ if (b != 0)
+ return false;
+ return true;
+ }
+
+ ///
+ /// Converts to a string containing the buffer described in hex
+ ///
+ public static string DescribeAsHex(byte[] buffer, string separator, int bytesPerLine)
+ {
+ StringBuilder sb = new StringBuilder();
+ int n = 0;
+ foreach (byte b in buffer)
+ {
+ sb.AppendFormat("{0:X2}{1}", b, separator);
+ if (++n % bytesPerLine == 0)
+ sb.Append("\r\n");
+ }
+ sb.Append("\r\n");
+ return sb.ToString();
+ }
+
+ ///
+ /// Decodes the buffer using the specified encoding, stopping at the first null
+ ///
+ public static string DecodeAsString(byte[] buffer, int offset, int length, Encoding encoding)
+ {
+ for (int n = 0; n < length; n++)
+ {
+ if (buffer[offset + n] == 0)
+ length = n;
+ }
+ return encoding.GetString(buffer, offset, length);
+ }
+
+ ///
+ /// Concatenates the given arrays into a single array.
+ ///
+ /// The arrays to concatenate
+ /// The concatenated resulting array.
+ public static byte[] Concat(params byte[][] byteArrays)
+ {
+ int size = 0;
+ foreach (byte[] btArray in byteArrays)
+ {
+ size += btArray.Length;
+ }
+
+ if (size <= 0)
+ {
+ return new byte[0];
+ }
+
+ byte[] result = new byte[size];
+ int idx = 0;
+ foreach (byte[] btArray in byteArrays)
+ {
+ Array.Copy(btArray, 0, result, idx, btArray.Length);
+ idx += btArray.Length;
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/NAudio/Utils/ByteEncoding.cs b/NAudio/Utils/ByteEncoding.cs
new file mode 100644
index 00000000..a2b44bec
--- /dev/null
+++ b/NAudio/Utils/ByteEncoding.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// An encoding for use with file types that have one byte per character
+ ///
+ public class ByteEncoding : Encoding
+ {
+ private ByteEncoding()
+ {
+ }
+
+ ///
+ /// The one and only instance of this class
+ ///
+ public static readonly ByteEncoding Instance = new ByteEncoding();
+
+ ///
+ ///
+ ///
+ public override int GetByteCount(char[] chars, int index, int count)
+ {
+ return count;
+ }
+
+ ///
+ ///
+ ///
+ public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex)
+ {
+ for (int n = 0; n < charCount; n++)
+ {
+ bytes[byteIndex + n] = (byte)chars[charIndex + n];
+ }
+ return charCount;
+ }
+
+ ///
+ ///
+ ///
+ public override int GetCharCount(byte[] bytes, int index, int count)
+ {
+ for (int n = 0; n < count; n++)
+ {
+ if (bytes[index + n] == 0)
+ return n;
+ }
+ return count;
+ }
+
+ ///
+ ///
+ ///
+ public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex)
+ {
+ for (int n = 0; n < byteCount; n++)
+ {
+ var b = bytes[byteIndex + n];
+ if (b == 0)
+ {
+ return n;
+ }
+ chars[charIndex + n] = (char)b;
+ }
+ return byteCount;
+ }
+
+ ///
+ ///
+ ///
+ public override int GetMaxCharCount(int byteCount)
+ {
+ return byteCount;
+ }
+
+ ///
+ ///
+ ///
+ public override int GetMaxByteCount(int charCount)
+ {
+ return charCount;
+ }
+ }
+}
diff --git a/NAudio/Utils/ChunkIdentifier.cs b/NAudio/Utils/ChunkIdentifier.cs
new file mode 100644
index 00000000..35b870fc
--- /dev/null
+++ b/NAudio/Utils/ChunkIdentifier.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// Chunk Identifier helpers
+ ///
+ public class ChunkIdentifier
+ {
+ ///
+ /// Chunk identifier to Int32 (replaces mmioStringToFOURCC)
+ ///
+ /// four character chunk identifier
+ /// Chunk identifier as int 32
+ public static int ChunkIdentifierToInt32(string s)
+ {
+ if (s.Length != 4) throw new ArgumentException("Must be a four character string");
+ var bytes = Encoding.UTF8.GetBytes(s);
+ if (bytes.Length != 4) throw new ArgumentException("Must encode to exactly four bytes");
+ return BitConverter.ToInt32(bytes, 0);
+ }
+ }
+}
diff --git a/NAudio/Utils/CircularBuffer.cs b/NAudio/Utils/CircularBuffer.cs
new file mode 100644
index 00000000..c0e2fdbd
--- /dev/null
+++ b/NAudio/Utils/CircularBuffer.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Diagnostics;
+
+namespace NAudio.Utils
+{
+ ///
+ /// A very basic circular buffer implementation
+ ///
+ public class CircularBuffer
+ {
+ private readonly byte[] buffer;
+ private readonly object lockObject;
+ private int writePosition;
+ private int readPosition;
+ private int byteCount;
+
+ ///
+ /// Create a new circular buffer
+ ///
+ /// Max buffer size in bytes
+ public CircularBuffer(int size)
+ {
+ buffer = new byte[size];
+ lockObject = new object();
+ }
+
+ ///
+ /// Write data to the buffer
+ ///
+ /// Data to write
+ /// Offset into data
+ /// Number of bytes to write
+ /// number of bytes written
+ public int Write(byte[] data, int offset, int count)
+ {
+ lock (lockObject)
+ {
+ var bytesWritten = 0;
+ if (count > buffer.Length - byteCount)
+ {
+ count = buffer.Length - byteCount;
+ }
+ // write to end
+ int writeToEnd = Math.Min(buffer.Length - writePosition, count);
+ Array.Copy(data, offset, buffer, writePosition, writeToEnd);
+ writePosition += writeToEnd;
+ writePosition %= buffer.Length;
+ bytesWritten += writeToEnd;
+ if (bytesWritten < count)
+ {
+ Debug.Assert(writePosition == 0);
+ // must have wrapped round. Write to start
+ Array.Copy(data, offset + bytesWritten, buffer, writePosition, count - bytesWritten);
+ writePosition += (count - bytesWritten);
+ bytesWritten = count;
+ }
+ byteCount += bytesWritten;
+ return bytesWritten;
+ }
+ }
+
+ ///
+ /// Read from the buffer
+ ///
+ /// Buffer to read into
+ /// Offset into read buffer
+ /// Bytes to read
+ /// Number of bytes actually read
+ public int Read(byte[] data, int offset, int count)
+ {
+ lock (lockObject)
+ {
+ if (count > byteCount)
+ {
+ count = byteCount;
+ }
+ int bytesRead = 0;
+ int readToEnd = Math.Min(buffer.Length - readPosition, count);
+ Array.Copy(buffer, readPosition, data, offset, readToEnd);
+ bytesRead += readToEnd;
+ readPosition += readToEnd;
+ readPosition %= buffer.Length;
+
+ if (bytesRead < count)
+ {
+ // must have wrapped round. Read from start
+ Debug.Assert(readPosition == 0);
+ Array.Copy(buffer, readPosition, data, offset + bytesRead, count - bytesRead);
+ readPosition += (count - bytesRead);
+ bytesRead = count;
+ }
+
+ byteCount -= bytesRead;
+ Debug.Assert(byteCount >= 0);
+ return bytesRead;
+ }
+ }
+
+ ///
+ /// Maximum length of this circular buffer
+ ///
+ public int MaxLength
+ {
+ get { return buffer.Length; }
+ }
+
+ ///
+ /// Number of bytes currently stored in the circular buffer
+ ///
+ public int Count
+ {
+ get { return byteCount; }
+ }
+
+ ///
+ /// Resets the buffer
+ ///
+ public void Reset()
+ {
+ byteCount = 0;
+ readPosition = 0;
+ writePosition = 0;
+ }
+
+ ///
+ /// Advances the buffer, discarding bytes
+ ///
+ /// Bytes to advance
+ public void Advance(int count)
+ {
+ if (count >= byteCount)
+ {
+ Reset();
+ }
+ else
+ {
+ byteCount -= count;
+ readPosition += count;
+ readPosition %= MaxLength;
+ }
+ }
+ }
+}
diff --git a/NAudio/Utils/Decibels.cs b/NAudio/Utils/Decibels.cs
new file mode 100644
index 00000000..e7bffc89
--- /dev/null
+++ b/NAudio/Utils/Decibels.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// A util class for conversions
+ ///
+ public class Decibels
+ {
+ // 20 / ln( 10 )
+ private const double LOG_2_DB = 8.6858896380650365530225783783321;
+
+ // ln( 10 ) / 20
+ private const double DB_2_LOG = 0.11512925464970228420089957273422;
+
+ ///
+ /// linear to dB conversion
+ ///
+ /// linear value
+ /// decibel value
+ public static double LinearToDecibels(double lin)
+ {
+ return Math.Log(lin) * LOG_2_DB;
+ }
+
+ ///
+ /// dB to linear conversion
+ ///
+ /// decibel value
+ /// linear value
+ public static double DecibelsToLinear(double dB)
+ {
+ return Math.Exp(dB * DB_2_LOG);
+ }
+
+ }
+}
diff --git a/NAudio/Utils/FieldDescriptionAttribute.cs b/NAudio/Utils/FieldDescriptionAttribute.cs
new file mode 100644
index 00000000..cb7d0c94
--- /dev/null
+++ b/NAudio/Utils/FieldDescriptionAttribute.cs
@@ -0,0 +1,33 @@
+using System;
+
+namespace NAudio.Utils
+{
+ ///
+ /// Allows us to add descriptions to interop members
+ ///
+ [AttributeUsage(AttributeTargets.Field)]
+ public class FieldDescriptionAttribute : Attribute
+ {
+ ///
+ /// The description
+ ///
+ public string Description { get; private set; }
+
+ ///
+ /// Field description
+ ///
+ public FieldDescriptionAttribute(string description)
+ {
+ Description = description;
+ }
+
+ ///
+ /// String representation
+ ///
+ ///
+ public override string ToString()
+ {
+ return Description;
+ }
+ }
+}
diff --git a/NAudio/Utils/FieldDescriptionHelper.cs b/NAudio/Utils/FieldDescriptionHelper.cs
new file mode 100644
index 00000000..583eb2bc
--- /dev/null
+++ b/NAudio/Utils/FieldDescriptionHelper.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// Helper to get descriptions
+ ///
+ public static class FieldDescriptionHelper
+ {
+ ///
+ /// Describes the Guid by looking for a FieldDescription attribute on the specified class
+ ///
+ public static string Describe(Type t, Guid guid)
+ {
+ // when we go to .NET 3.5, use LINQ for this
+ foreach (var f in t
+#if NETFX_CORE
+ .GetRuntimeFields())
+#else
+ .GetFields(BindingFlags.Static | BindingFlags.Public))
+#endif
+ {
+ if (f.IsPublic && f.IsStatic && f.FieldType == typeof (Guid) && (Guid) f.GetValue(null) == guid)
+ {
+ foreach (var a in f.GetCustomAttributes(false))
+ {
+ var d = a as FieldDescriptionAttribute;
+ if (d != null)
+ {
+ return d.Description;
+ }
+ }
+ // no attribute, return the name
+ return f.Name;
+ }
+ }
+ return guid.ToString();
+ }
+ }
+}
diff --git a/NAudio/Utils/HResult.cs b/NAudio/Utils/HResult.cs
new file mode 100644
index 00000000..4b435bcf
--- /dev/null
+++ b/NAudio/Utils/HResult.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// HResult
+ ///
+ public static class HResult
+ {
+ ///
+ /// S_OK
+ ///
+ public const int S_OK = 0;
+ ///
+ /// S_FALSE
+ ///
+ public const int S_FALSE = 1;
+ ///
+ /// E_INVALIDARG (from winerror.h)
+ ///
+ public const int E_INVALIDARG = unchecked((int)0x80000003);
+ ///
+ /// MAKE_HRESULT macro
+ ///
+ public static int MAKE_HRESULT(int sev, int fac, int code)
+ {
+ return (int) (((uint)sev) << 31 | ((uint)fac) << 16 | ((uint)code));
+ }
+
+ const int FACILITY_AAF = 18;
+ const int FACILITY_ACS = 20;
+ const int FACILITY_BACKGROUNDCOPY = 32;
+ const int FACILITY_CERT = 11;
+ const int FACILITY_COMPLUS = 17;
+ const int FACILITY_CONFIGURATION = 33;
+ const int FACILITY_CONTROL = 10;
+ const int FACILITY_DISPATCH = 2;
+ const int FACILITY_DPLAY = 21;
+ const int FACILITY_HTTP = 25;
+ const int FACILITY_INTERNET = 12;
+ const int FACILITY_ITF = 4;
+ const int FACILITY_MEDIASERVER = 13;
+ const int FACILITY_MSMQ = 14;
+ const int FACILITY_NULL = 0;
+ const int FACILITY_RPC = 1;
+ const int FACILITY_SCARD = 16;
+ const int FACILITY_SECURITY = 9;
+ const int FACILITY_SETUPAPI = 15;
+ const int FACILITY_SSPI = 9;
+ const int FACILITY_STORAGE = 3;
+ const int FACILITY_SXS = 23;
+ const int FACILITY_UMI = 22;
+ const int FACILITY_URT = 19;
+ const int FACILITY_WIN32 = 7;
+ const int FACILITY_WINDOWS = 8;
+ const int FACILITY_WINDOWS_CE = 24;
+
+ ///
+ /// Helper to deal with the fact that in Win Store apps,
+ /// the HResult property name has changed
+ ///
+ /// COM Exception
+ /// The HResult
+ public static int GetHResult(this COMException exception)
+ {
+#if NETFX_CORE
+ return exception.HResult;
+#else
+ return exception.ErrorCode;
+#endif
+ }
+ }
+
+}
diff --git a/NAudio/Utils/IEEE.cs b/NAudio/Utils/IEEE.cs
new file mode 100644
index 00000000..274733ef
--- /dev/null
+++ b/NAudio/Utils/IEEE.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// Methods for converting between IEEE 80-bit extended double precision
+ /// and standard C# double precision.
+ ///
+ public static class IEEE
+ {
+ #region Helper Methods
+ private static double UnsignedToFloat(ulong u)
+ {
+ return (((double)((long)(u - 2147483647L - 1))) + 2147483648.0);
+ }
+
+ private static double ldexp(double x, int exp)
+ {
+ return x * Math.Pow(2, exp);
+ }
+
+ private static double frexp(double x, out int exp)
+ {
+ exp = (int)Math.Floor(Math.Log(x) / Math.Log(2)) + 1;
+ return 1 - (Math.Pow(2, exp) - x) / Math.Pow(2, exp);
+ }
+
+ private static ulong FloatToUnsigned(double f)
+ {
+ return ((ulong)(((long)(f - 2147483648.0)) + 2147483647L) + 1);
+ }
+ #endregion
+
+ #region ConvertToIeeeExtended
+ ///
+ /// Converts a C# double precision number to an 80-bit
+ /// IEEE extended double precision number (occupying 10 bytes).
+ ///
+ /// The double precision number to convert to IEEE extended.
+ /// An array of 10 bytes containing the IEEE extended number.
+ public static byte[] ConvertToIeeeExtended(double num)
+ {
+ int sign;
+ int expon;
+ double fMant, fsMant;
+ ulong hiMant, loMant;
+
+ if (num < 0)
+ {
+ sign = 0x8000;
+ num *= -1;
+ }
+ else
+ {
+ sign = 0;
+ }
+
+ if (num == 0)
+ {
+ expon = 0; hiMant = 0; loMant = 0;
+ }
+ else
+ {
+ fMant = frexp(num, out expon);
+ if ((expon > 16384) || !(fMant < 1))
+ { // Infinity or NaN
+ expon = sign | 0x7FFF; hiMant = 0; loMant = 0; // infinity
+ }
+ else
+ { // Finite
+ expon += 16382;
+ if (expon < 0)
+ { // denormalized
+ fMant = ldexp(fMant, expon);
+ expon = 0;
+ }
+ expon |= sign;
+ fMant = ldexp(fMant, 32);
+ fsMant = Math.Floor(fMant);
+ hiMant = FloatToUnsigned(fsMant);
+ fMant = ldexp(fMant - fsMant, 32);
+ fsMant = Math.Floor(fMant);
+ loMant = FloatToUnsigned(fsMant);
+ }
+ }
+
+ byte[] bytes = new byte[10];
+
+ bytes[0] = (byte)(expon >> 8);
+ bytes[1] = (byte)(expon);
+ bytes[2] = (byte)(hiMant >> 24);
+ bytes[3] = (byte)(hiMant >> 16);
+ bytes[4] = (byte)(hiMant >> 8);
+ bytes[5] = (byte)(hiMant);
+ bytes[6] = (byte)(loMant >> 24);
+ bytes[7] = (byte)(loMant >> 16);
+ bytes[8] = (byte)(loMant >> 8);
+ bytes[9] = (byte)(loMant);
+
+ return bytes;
+ }
+ #endregion
+
+ #region ConvertFromIeeeExtended
+ ///
+ /// Converts an IEEE 80-bit extended precision number to a
+ /// C# double precision number.
+ ///
+ /// The 80-bit IEEE extended number (as an array of 10 bytes).
+ /// A C# double precision number that is a close representation of the IEEE extended number.
+ public static double ConvertFromIeeeExtended(byte[] bytes)
+ {
+ if (bytes.Length != 10) throw new Exception("Incorrect length for IEEE extended.");
+ double f;
+ int expon;
+ uint hiMant, loMant;
+
+ expon = ((bytes[0] & 0x7F) << 8) | bytes[1];
+ hiMant = (uint)((bytes[2] << 24) | (bytes[3] << 16) | (bytes[4] << 8) | bytes[5]);
+ loMant = (uint)((bytes[6] << 24) | (bytes[7] << 16) | (bytes[8] << 8) | bytes[9]);
+
+ if (expon == 0 && hiMant == 0 && loMant == 0)
+ {
+ f = 0;
+ }
+ else
+ {
+ if (expon == 0x7FFF) /* Infinity or NaN */
+ {
+ f = double.NaN;
+ }
+ else
+ {
+ expon -= 16383;
+ f = ldexp(UnsignedToFloat(hiMant), expon -= 31);
+ f += ldexp(UnsignedToFloat(loMant), expon -= 32);
+ }
+ }
+
+ if ((bytes[0] & 0x80) == 0x80) return -f;
+ else return f;
+ }
+ #endregion
+ }
+}
diff --git a/NAudio/Utils/IgnoreDisposeStream.cs b/NAudio/Utils/IgnoreDisposeStream.cs
new file mode 100644
index 00000000..23b4a808
--- /dev/null
+++ b/NAudio/Utils/IgnoreDisposeStream.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.Utils
+{
+ ///
+ /// Pass-through stream that ignores Dispose
+ /// Useful for dealing with MemoryStreams that you want to re-use
+ ///
+ public class IgnoreDisposeStream : Stream
+ {
+ ///
+ /// The source stream all other methods fall through to
+ ///
+ public Stream SourceStream { get; private set; }
+
+ ///
+ /// If true the Dispose will be ignored, if false, will pass through to the SourceStream
+ /// Set to true by default
+ ///
+ public bool IgnoreDispose { get; set; }
+
+ ///
+ /// Creates a new IgnoreDisposeStream
+ ///
+ /// The source stream
+ public IgnoreDisposeStream(Stream sourceStream)
+ {
+ SourceStream = sourceStream;
+ IgnoreDispose = true;
+ }
+
+ ///
+ /// Can Read
+ ///
+ public override bool CanRead
+ {
+ get { return SourceStream.CanRead; }
+ }
+
+ ///
+ /// Can Seek
+ ///
+ public override bool CanSeek
+ {
+ get { return SourceStream.CanSeek; }
+ }
+
+ ///
+ /// Can write to the underlying stream
+ ///
+ public override bool CanWrite
+ {
+ get { return SourceStream.CanWrite; }
+ }
+
+ ///
+ /// Flushes the underlying stream
+ ///
+ public override void Flush()
+ {
+ SourceStream.Flush();
+ }
+
+ ///
+ /// Gets the length of the underlying stream
+ ///
+ public override long Length
+ {
+ get { return SourceStream.Length; }
+ }
+
+ ///
+ /// Gets or sets the position of the underlying stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return SourceStream.Position;
+ }
+ set
+ {
+ SourceStream.Position = value;
+ }
+ }
+
+ ///
+ /// Reads from the underlying stream
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return SourceStream.Read(buffer, offset, count);
+ }
+
+ ///
+ /// Seeks on the underlying stream
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ return SourceStream.Seek(offset, origin);
+ }
+
+ ///
+ /// Sets the length of the underlying stream
+ ///
+ public override void SetLength(long value)
+ {
+ SourceStream.SetLength(value);
+ }
+
+ ///
+ /// Writes to the underlying stream
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ SourceStream.Write(buffer, offset, count);
+ }
+
+ ///
+ /// Dispose - by default (IgnoreDispose = true) will do nothing,
+ /// leaving the underlying stream undisposed
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (!IgnoreDispose)
+ {
+ SourceStream.Dispose();
+ SourceStream = null;
+ }
+ }
+ }
+}
diff --git a/NAudio/Utils/MergeSort.cs b/NAudio/Utils/MergeSort.cs
new file mode 100644
index 00000000..150d8cfb
--- /dev/null
+++ b/NAudio/Utils/MergeSort.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ class MergeSort
+ {
+ ///
+ /// In-place and stable implementation of MergeSort
+ ///
+ static void Sort(IList list, int lowIndex, int highIndex, IComparer comparer)
+ {
+ if (lowIndex >= highIndex)
+ {
+ return;
+ }
+
+
+ int midIndex = (lowIndex + highIndex) / 2;
+
+
+ // Partition the list into two lists and Sort them recursively
+ Sort(list, lowIndex, midIndex, comparer);
+ Sort(list, midIndex + 1, highIndex, comparer);
+
+ // Merge the two sorted lists
+ int endLow = midIndex;
+ int startHigh = midIndex + 1;
+
+
+ while ((lowIndex <= endLow) && (startHigh <= highIndex))
+ {
+ // MRH, if use < 0 sort is not stable
+ if (comparer.Compare(list[lowIndex], list[startHigh]) <= 0)
+ {
+ lowIndex++;
+ }
+ else
+ {
+ // list[lowIndex] > list[startHigh]
+ // The next element comes from the second list,
+ // move the list[start_hi] element into the next
+ // position and shuffle all the other elements up.
+ T t = list[startHigh];
+
+ for (int k = startHigh - 1; k >= lowIndex; k--)
+ {
+ list[k + 1] = list[k];
+ }
+
+ list[lowIndex] = t;
+ lowIndex++;
+ endLow++;
+ startHigh++;
+ }
+ }
+ }
+
+ ///
+ /// MergeSort a list of comparable items
+ ///
+ public static void Sort(IList list) where T : IComparable
+ {
+ Sort(list, 0, list.Count - 1, Comparer.Default);
+ }
+
+ ///
+ /// MergeSort a list
+ ///
+ public static void Sort(IList list, IComparer comparer)
+ {
+ Sort(list, 0, list.Count - 1, comparer);
+ }
+ }
+}
diff --git a/NAudio/Utils/NativeMethods.cs b/NAudio/Utils/NativeMethods.cs
new file mode 100644
index 00000000..6ec1226f
--- /dev/null
+++ b/NAudio/Utils/NativeMethods.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace NAudio.Utils
+{
+ ///
+ /// General purpose native methods for internal NAudio use
+ ///
+ class NativeMethods
+ {
+#if !NETFX_CORE
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr LoadLibrary(string dllToLoad);
+#endif
+
+ [DllImport("kernel32.dll")]
+ public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
+
+ [DllImport("kernel32.dll")]
+ public static extern bool FreeLibrary(IntPtr hModule);
+ }
+}
diff --git a/NAudio/Utils/ProgressLog.Designer.cs b/NAudio/Utils/ProgressLog.Designer.cs
new file mode 100644
index 00000000..f7f9977f
--- /dev/null
+++ b/NAudio/Utils/ProgressLog.Designer.cs
@@ -0,0 +1,62 @@
+namespace NAudio.Utils
+{
+ partial class ProgressLog
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.richTextBoxLog = new System.Windows.Forms.RichTextBox();
+ this.SuspendLayout();
+ //
+ // richTextBoxLog
+ //
+ this.richTextBoxLog.BorderStyle = System.Windows.Forms.BorderStyle.None;
+ this.richTextBoxLog.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.richTextBoxLog.Location = new System.Drawing.Point(1, 1);
+ this.richTextBoxLog.Name = "richTextBoxLog";
+ this.richTextBoxLog.ReadOnly = true;
+ this.richTextBoxLog.Size = new System.Drawing.Size(311, 129);
+ this.richTextBoxLog.TabIndex = 0;
+ this.richTextBoxLog.Text = "";
+ //
+ // ProgressLog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.SystemColors.ControlDarkDark;
+ this.Controls.Add(this.richTextBoxLog);
+ this.Name = "ProgressLog";
+ this.Padding = new System.Windows.Forms.Padding(1);
+ this.Size = new System.Drawing.Size(313, 131);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.RichTextBox richTextBoxLog;
+ }
+}
diff --git a/NAudio/Utils/ProgressLog.cs b/NAudio/Utils/ProgressLog.cs
new file mode 100644
index 00000000..9dd9466a
--- /dev/null
+++ b/NAudio/Utils/ProgressLog.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Text;
+using System.Windows.Forms;
+
+namespace NAudio.Utils
+{
+ ///
+ /// A thread-safe Progress Log Control
+ ///
+ public partial class ProgressLog : UserControl
+ {
+ ///
+ /// Creates a new progress log control
+ ///
+ public ProgressLog()
+ {
+ InitializeComponent();
+ }
+
+
+
+ ///
+ /// The contents of the log as text
+ ///
+ public new string Text
+ {
+ get
+ {
+ return richTextBoxLog.Text;
+ }
+ }
+
+
+
+ delegate void LogMessageDelegate(Color color, string message);
+
+ ///
+ /// Log a message
+ ///
+ public void LogMessage(Color color, string message)
+ {
+ if (richTextBoxLog.InvokeRequired)
+ {
+ this.Invoke(new LogMessageDelegate(LogMessage), new object[] { color, message });
+ }
+ else
+ {
+ richTextBoxLog.SelectionStart = richTextBoxLog.TextLength;
+ richTextBoxLog.SelectionColor = color;
+ richTextBoxLog.AppendText(message);
+ richTextBoxLog.AppendText(System.Environment.NewLine);
+ }
+ }
+
+ delegate void ClearLogDelegate();
+
+ ///
+ /// Clear the log
+ ///
+ public void ClearLog()
+ {
+ if (richTextBoxLog.InvokeRequired)
+ {
+ this.Invoke(new ClearLogDelegate(ClearLog), new object[] { });
+ }
+ else
+ {
+ richTextBoxLog.Clear();
+ }
+ }
+ }
+}
diff --git a/NAudio/Utils/ProgressLog.resx b/NAudio/Utils/ProgressLog.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/NAudio/Utils/ProgressLog.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudio/Utils/WavePositionExtensions.cs b/NAudio/Utils/WavePositionExtensions.cs
new file mode 100644
index 00000000..4d122548
--- /dev/null
+++ b/NAudio/Utils/WavePositionExtensions.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+
+namespace NAudio.Utils
+{
+ ///
+ /// WavePosition extension methods
+ ///
+ public static class WavePositionExtensions
+ {
+ ///
+ /// Get Position as timespan
+ ///
+ public static TimeSpan GetPositionTimeSpan(this IWavePosition @this)
+ {
+ var pos = @this.GetPosition() / (@this.OutputWaveFormat.Channels * @this.OutputWaveFormat.BitsPerSample / 8);
+ return TimeSpan.FromMilliseconds(pos * 1000.0 / @this.OutputWaveFormat.SampleRate);
+ }
+ }
+}
diff --git a/NAudio/Wave/Asio/ASIODriver.cs b/NAudio/Wave/Asio/ASIODriver.cs
new file mode 100644
index 00000000..1c9545e1
--- /dev/null
+++ b/NAudio/Wave/Asio/ASIODriver.cs
@@ -0,0 +1,461 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32;
+
+namespace NAudio.Wave.Asio
+{
+ ///
+ /// Main ASIODriver Class. To use this class, you need to query first the GetASIODriverNames() and
+ /// then use the GetASIODriverByName to instantiate the correct ASIODriver.
+ /// This is the first ASIODriver binding fully implemented in C#!
+ ///
+ /// Contributor: Alexandre Mutel - email: alexandre_mutel at yahoo.fr
+ ///
+ internal class ASIODriver
+ {
+ IntPtr pASIOComObject;
+ IntPtr pinnedcallbacks;
+ private ASIODriverVTable asioDriverVTable;
+
+ private ASIODriver()
+ {
+ }
+
+ ///
+ /// Gets the ASIO driver names installed.
+ ///
+ /// a list of driver names. Use this name to GetASIODriverByName
+ public static String[] GetASIODriverNames()
+ {
+ RegistryKey regKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\ASIO");
+ String[] names = new string[0];
+ if (regKey != null)
+ {
+ names = regKey.GetSubKeyNames();
+ regKey.Close();
+ }
+ return names;
+ }
+
+ ///
+ /// Instantiate a ASIODriver given its name.
+ ///
+ /// The name of the driver
+ /// an ASIODriver instance
+ public static ASIODriver GetASIODriverByName(String name)
+ {
+ RegistryKey regKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\ASIO\\" + name);
+ if (regKey == null)
+ {
+ throw new ArgumentException(String.Format("Driver Name {0} doesn't exist", name));
+ }
+ String guid = regKey.GetValue("CLSID").ToString();
+ return GetASIODriverByGuid(new Guid(guid));
+ }
+
+ ///
+ /// Instantiate the ASIO driver by GUID.
+ ///
+ /// The GUID.
+ /// an ASIODriver instance
+ public static ASIODriver GetASIODriverByGuid(Guid guid)
+ {
+ ASIODriver driver = new ASIODriver();
+ driver.initFromGuid(guid);
+ return driver;
+ }
+
+ ///
+ /// Inits the ASIODriver..
+ ///
+ /// The sys handle.
+ ///
+ public bool init(IntPtr sysHandle)
+ {
+ int ret = asioDriverVTable.init(pASIOComObject, sysHandle);
+ return ret == 1;
+ }
+
+ ///
+ /// Gets the name of the driver.
+ ///
+ ///
+ public String getDriverName()
+ {
+ StringBuilder name = new StringBuilder(256);
+ asioDriverVTable.getDriverName(pASIOComObject, name);
+ return name.ToString();
+ }
+
+ ///
+ /// Gets the driver version.
+ ///
+ ///
+ public int getDriverVersion() {
+ return asioDriverVTable.getDriverVersion(pASIOComObject);
+ }
+
+ ///
+ /// Gets the error message.
+ ///
+ ///
+ public String getErrorMessage()
+ {
+ StringBuilder errorMessage = new StringBuilder(256);
+ asioDriverVTable.getErrorMessage(pASIOComObject, errorMessage);
+ return errorMessage.ToString();
+ }
+
+ ///
+ /// Starts this instance.
+ ///
+ public void start()
+ {
+ handleException(asioDriverVTable.start(pASIOComObject),"start");
+ }
+
+ ///
+ /// Stops this instance.
+ ///
+ public ASIOError stop()
+ {
+ return asioDriverVTable.stop(pASIOComObject);
+ }
+
+ ///
+ /// Gets the channels.
+ ///
+ /// The num input channels.
+ /// The num output channels.
+ public void getChannels(out int numInputChannels, out int numOutputChannels)
+ {
+ handleException(asioDriverVTable.getChannels(pASIOComObject, out numInputChannels, out numOutputChannels), "getChannels");
+ }
+
+ ///
+ /// Gets the latencies (n.b. does not throw an exception)
+ ///
+ /// The input latency.
+ /// The output latency.
+ public ASIOError GetLatencies(out int inputLatency, out int outputLatency)
+ {
+ return asioDriverVTable.getLatencies(pASIOComObject, out inputLatency, out outputLatency);
+ }
+
+ ///
+ /// Gets the size of the buffer.
+ ///
+ /// Size of the min.
+ /// Size of the max.
+ /// Size of the preferred.
+ /// The granularity.
+ public void getBufferSize(out int minSize, out int maxSize, out int preferredSize, out int granularity)
+ {
+ handleException(asioDriverVTable.getBufferSize(pASIOComObject, out minSize, out maxSize, out preferredSize, out granularity), "getBufferSize");
+ }
+
+ ///
+ /// Determines whether this instance can use the specified sample rate.
+ ///
+ /// The sample rate.
+ ///
+ /// true if this instance [can sample rate] the specified sample rate; otherwise, false .
+ ///
+ public bool canSampleRate(double sampleRate)
+ {
+ ASIOError error = asioDriverVTable.canSampleRate(pASIOComObject, sampleRate);
+ if (error == ASIOError.ASE_NoClock)
+ {
+ return false;
+ }
+ if ( error == ASIOError.ASE_OK )
+ {
+ return true;
+ }
+ handleException(error, "canSampleRate");
+ return false;
+ }
+
+ ///
+ /// Gets the sample rate.
+ ///
+ ///
+ public double getSampleRate()
+ {
+ double sampleRate;
+ handleException(asioDriverVTable.getSampleRate(pASIOComObject, out sampleRate), "getSampleRate");
+ return sampleRate;
+ }
+
+ ///
+ /// Sets the sample rate.
+ ///
+ /// The sample rate.
+ public void setSampleRate(double sampleRate)
+ {
+ handleException(asioDriverVTable.setSampleRate(pASIOComObject, sampleRate), "setSampleRate");
+ }
+
+ ///
+ /// Gets the clock sources.
+ ///
+ /// The clocks.
+ /// The num sources.
+ public void getClockSources(out long clocks, int numSources)
+ {
+ handleException(asioDriverVTable.getClockSources(pASIOComObject, out clocks,numSources), "getClockSources");
+ }
+
+ ///
+ /// Sets the clock source.
+ ///
+ /// The reference.
+ public void setClockSource(int reference)
+ {
+ handleException(asioDriverVTable.setClockSource(pASIOComObject, reference), "setClockSources");
+ }
+
+ ///
+ /// Gets the sample position.
+ ///
+ /// The sample pos.
+ /// The time stamp.
+ public void getSamplePosition(out long samplePos, ref ASIO64Bit timeStamp)
+ {
+ handleException(asioDriverVTable.getSamplePosition(pASIOComObject, out samplePos, ref timeStamp), "getSamplePosition");
+ }
+
+ ///
+ /// Gets the channel info.
+ ///
+ /// The channel number.
+ /// if set to true [true for input info].
+ ///
+ public ASIOChannelInfo getChannelInfo(int channelNumber, bool trueForInputInfo)
+ {
+ ASIOChannelInfo info = new ASIOChannelInfo {channel = channelNumber, isInput = trueForInputInfo};
+ handleException(asioDriverVTable.getChannelInfo(pASIOComObject, ref info), "getChannelInfo");
+ return info;
+ }
+
+ ///
+ /// Creates the buffers.
+ ///
+ /// The buffer infos.
+ /// The num channels.
+ /// Size of the buffer.
+ /// The callbacks.
+ public void createBuffers(IntPtr bufferInfos, int numChannels, int bufferSize, ref ASIOCallbacks callbacks)
+ {
+ // next two lines suggested by droidi on codeplex issue tracker
+ pinnedcallbacks = Marshal.AllocHGlobal(Marshal.SizeOf(callbacks));
+ Marshal.StructureToPtr(callbacks, pinnedcallbacks, false);
+ handleException(asioDriverVTable.createBuffers(pASIOComObject, bufferInfos, numChannels, bufferSize, pinnedcallbacks), "createBuffers");
+ }
+
+ ///
+ /// Disposes the buffers.
+ ///
+ public ASIOError disposeBuffers()
+ {
+ ASIOError result = asioDriverVTable.disposeBuffers(pASIOComObject);
+ Marshal.FreeHGlobal(pinnedcallbacks);
+ return result;
+ }
+
+ ///
+ /// Controls the panel.
+ ///
+ public void controlPanel()
+ {
+ handleException(asioDriverVTable.controlPanel(pASIOComObject), "controlPanel");
+ }
+
+ ///
+ /// Futures the specified selector.
+ ///
+ /// The selector.
+ /// The opt.
+ public void future(int selector, IntPtr opt)
+ {
+ handleException(asioDriverVTable.future(pASIOComObject, selector, opt), "future");
+ }
+
+ ///
+ /// Notifies OutputReady to the ASIODriver.
+ ///
+ ///
+ public ASIOError outputReady()
+ {
+ return asioDriverVTable.outputReady(pASIOComObject);
+ }
+
+ ///
+ /// Releases this instance.
+ ///
+ public void ReleaseComASIODriver()
+ {
+ Marshal.Release(pASIOComObject);
+ }
+
+ ///
+ /// Handles the exception. Throws an exception based on the error.
+ ///
+ /// The error to check.
+ /// Method name
+ private void handleException(ASIOError error, String methodName)
+ {
+ if (error != ASIOError.ASE_OK && error != ASIOError.ASE_SUCCESS)
+ {
+ ASIOException asioException = new ASIOException(String.Format("Error code [{0}] while calling ASIO method <{1}>, {2}", ASIOException.getErrorName(error), methodName, this.getErrorMessage()));
+ asioException.Error = error;
+ throw asioException;
+ }
+ }
+
+ ///
+ /// Inits the vTable method from GUID. This is a tricky part of this class.
+ ///
+ /// The ASIO GUID.
+ private void initFromGuid(Guid ASIOGuid)
+ {
+ const uint CLSCTX_INPROC_SERVER = 1;
+ // Start to query the virtual table a index 3 (init method of ASIODriver)
+ const int INDEX_VTABLE_FIRST_METHOD = 3;
+
+ // Pointer to the ASIO object
+ // USE CoCreateInstance instead of builtin COM-Class instantiation,
+ // because the ASIODriver expect to have the ASIOGuid used for both COM Object and COM interface
+ // The CoCreateInstance is working only in STAThread mode.
+ int hresult = CoCreateInstance(ref ASIOGuid, IntPtr.Zero, CLSCTX_INPROC_SERVER, ref ASIOGuid, out pASIOComObject);
+ if ( hresult != 0 )
+ {
+ throw new COMException("Unable to instantiate ASIO. Check if STAThread is set",hresult);
+ }
+
+ // The first pointer at the adress of the ASIO Com Object is a pointer to the
+ // C++ Virtual table of the object.
+ // Gets a pointer to VTable.
+ IntPtr pVtable = Marshal.ReadIntPtr(pASIOComObject);
+
+ // Instantiate our Virtual table mapping
+ asioDriverVTable = new ASIODriverVTable();
+
+ // This loop is going to retrieve the pointer from the C++ VirtualTable
+ // and attach an internal delegate in order to call the method on the COM Object.
+ FieldInfo[] fieldInfos = typeof (ASIODriverVTable).GetFields();
+ for (int i = 0; i < fieldInfos.Length; i++)
+ {
+ FieldInfo fieldInfo = fieldInfos[i];
+ // Read the method pointer from the VTable
+ IntPtr pPointerToMethodInVTable = Marshal.ReadIntPtr(pVtable, (i + INDEX_VTABLE_FIRST_METHOD) * IntPtr.Size);
+ // Instantiate a delegate
+ object methodDelegate = Marshal.GetDelegateForFunctionPointer(pPointerToMethodInVTable, fieldInfo.FieldType);
+ // Store the delegate in our C# VTable
+ fieldInfo.SetValue(asioDriverVTable, methodDelegate);
+ }
+ }
+
+ ///
+ /// Internal VTable structure to store all the delegates to the C++ COM method.
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
+ private class ASIODriverVTable
+ {
+ //3 virtual ASIOBool init(void *sysHandle) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate int ASIOInit(IntPtr _pUnknown, IntPtr sysHandle);
+ public ASIOInit init = null;
+ //4 virtual void getDriverName(char *name) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate void ASIOgetDriverName(IntPtr _pUnknown, StringBuilder name);
+ public ASIOgetDriverName getDriverName = null;
+ //5 virtual long getDriverVersion() = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate int ASIOgetDriverVersion(IntPtr _pUnknown);
+ public ASIOgetDriverVersion getDriverVersion = null;
+ //6 virtual void getErrorMessage(char *string) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate void ASIOgetErrorMessage(IntPtr _pUnknown, StringBuilder errorMessage);
+ public ASIOgetErrorMessage getErrorMessage = null;
+ //7 virtual ASIOError start() = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOstart(IntPtr _pUnknown);
+ public ASIOstart start = null;
+ //8 virtual ASIOError stop() = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOstop(IntPtr _pUnknown);
+ public ASIOstop stop = null;
+ //9 virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOgetChannels(IntPtr _pUnknown, out int numInputChannels, out int numOutputChannels);
+ public ASIOgetChannels getChannels = null;
+ //10 virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOgetLatencies(IntPtr _pUnknown, out int inputLatency, out int outputLatency);
+ public ASIOgetLatencies getLatencies = null;
+ //11 virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOgetBufferSize(IntPtr _pUnknown, out int minSize, out int maxSize, out int preferredSize, out int granularity);
+ public ASIOgetBufferSize getBufferSize = null;
+ //12 virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOcanSampleRate(IntPtr _pUnknown, double sampleRate);
+ public ASIOcanSampleRate canSampleRate = null;
+ //13 virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOgetSampleRate(IntPtr _pUnknown, out double sampleRate);
+ public ASIOgetSampleRate getSampleRate = null;
+ //14 virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOsetSampleRate(IntPtr _pUnknown, double sampleRate);
+ public ASIOsetSampleRate setSampleRate = null;
+ //15 virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOgetClockSources(IntPtr _pUnknown, out long clocks, int numSources);
+ public ASIOgetClockSources getClockSources = null;
+ //16 virtual ASIOError setClockSource(long reference) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOsetClockSource(IntPtr _pUnknown, int reference);
+ public ASIOsetClockSource setClockSource = null;
+ //17 virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOgetSamplePosition(IntPtr _pUnknown, out long samplePos, ref ASIO64Bit timeStamp);
+ public ASIOgetSamplePosition getSamplePosition = null;
+ //18 virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOgetChannelInfo(IntPtr _pUnknown, ref ASIOChannelInfo info);
+ public ASIOgetChannelInfo getChannelInfo = null;
+ //19 virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ // public delegate ASIOError ASIOcreateBuffers(IntPtr _pUnknown, ref ASIOBufferInfo[] bufferInfos, int numChannels, int bufferSize, ref ASIOCallbacks callbacks);
+ public delegate ASIOError ASIOcreateBuffers(IntPtr _pUnknown, IntPtr bufferInfos, int numChannels, int bufferSize, IntPtr callbacks);
+ public ASIOcreateBuffers createBuffers = null;
+ //20 virtual ASIOError disposeBuffers() = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOdisposeBuffers(IntPtr _pUnknown);
+ public ASIOdisposeBuffers disposeBuffers = null;
+ //21 virtual ASIOError controlPanel() = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOcontrolPanel(IntPtr _pUnknown);
+ public ASIOcontrolPanel controlPanel = null;
+ //22 virtual ASIOError future(long selector,void *opt) = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOfuture(IntPtr _pUnknown, int selector, IntPtr opt);
+ public ASIOfuture future = null;
+ //23 virtual ASIOError outputReady() = 0;
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ public delegate ASIOError ASIOoutputReady(IntPtr _pUnknown);
+ public ASIOoutputReady outputReady = null;
+ }
+
+ [DllImport("ole32.Dll")]
+ static private extern int CoCreateInstance(ref Guid clsid,
+ IntPtr inner,
+ uint context,
+ ref Guid uuid,
+ out IntPtr rReturnedComObject);
+ }
+}
diff --git a/NAudio/Wave/Asio/ASIODriverExt.cs b/NAudio/Wave/Asio/ASIODriverExt.cs
new file mode 100644
index 00000000..3578367b
--- /dev/null
+++ b/NAudio/Wave/Asio/ASIODriverExt.cs
@@ -0,0 +1,398 @@
+using System;
+
+namespace NAudio.Wave.Asio
+{
+ ///
+ /// Callback used by the ASIODriverExt to get wave data
+ ///
+ internal delegate void ASIOFillBufferCallback(IntPtr[] inputChannels, IntPtr[] outputChannels);
+
+ ///
+ /// ASIODriverExt is a simplified version of the ASIODriver. It provides an easier
+ /// way to access the capabilities of the Driver and implement the callbacks necessary
+ /// for feeding the driver.
+ /// Implementation inspired from Rob Philpot's with a managed C++ ASIO wrapper BlueWave.Interop.Asio
+ /// http://www.codeproject.com/KB/mcpp/Asio.Net.aspx
+ ///
+ /// Contributor: Alexandre Mutel - email: alexandre_mutel at yahoo.fr
+ ///
+ internal class ASIODriverExt
+ {
+ private ASIODriver driver;
+ private ASIOCallbacks callbacks;
+ private AsioDriverCapability capability;
+ private ASIOBufferInfo[] bufferInfos;
+ private bool isOutputReadySupported;
+ private IntPtr[] currentOutputBuffers;
+ private IntPtr[] currentInputBuffers;
+ private int numberOfOutputChannels;
+ private int numberOfInputChannels;
+ private ASIOFillBufferCallback fillBufferCallback;
+ private int bufferSize;
+ private int outputChannelOffset;
+ private int inputChannelOffset;
+
+ ///
+ /// Initializes a new instance of the class based on an already
+ /// instantiated ASIODriver instance.
+ ///
+ /// A ASIODriver already instantiated.
+ public ASIODriverExt(ASIODriver driver)
+ {
+ this.driver = driver;
+
+ if (!driver.init(IntPtr.Zero))
+ {
+ throw new InvalidOperationException(driver.getErrorMessage());
+ }
+
+ callbacks = new ASIOCallbacks();
+ callbacks.pasioMessage = AsioMessageCallBack;
+ callbacks.pbufferSwitch = BufferSwitchCallBack;
+ callbacks.pbufferSwitchTimeInfo = BufferSwitchTimeInfoCallBack;
+ callbacks.psampleRateDidChange = SampleRateDidChangeCallBack;
+
+ BuildCapabilities();
+ }
+
+ ///
+ /// Allows adjustment of which is the first output channel we write to
+ ///
+ /// Output Channel offset
+ /// Input Channel offset
+ public void SetChannelOffset(int outputChannelOffset, int inputChannelOffset)
+ {
+ if (outputChannelOffset + numberOfOutputChannels <= Capabilities.NbOutputChannels)
+ {
+ this.outputChannelOffset = outputChannelOffset;
+ }
+ else
+ {
+ throw new ArgumentException("Invalid channel offset");
+ }
+ if (inputChannelOffset + numberOfInputChannels <= Capabilities.NbInputChannels)
+ {
+ this.inputChannelOffset = inputChannelOffset;
+ }
+ else
+ {
+ throw new ArgumentException("Invalid channel offset");
+ }
+
+ }
+
+ ///
+ /// Gets the driver used.
+ ///
+ /// The ASIOdriver.
+ public ASIODriver Driver
+ {
+ get { return driver; }
+ }
+
+ ///
+ /// Starts playing the buffers.
+ ///
+ public void Start()
+ {
+ driver.start();
+ }
+
+ ///
+ /// Stops playing the buffers.
+ ///
+ public void Stop()
+ {
+ driver.stop();
+ }
+
+ ///
+ /// Shows the control panel.
+ ///
+ public void ShowControlPanel()
+ {
+ driver.controlPanel();
+ }
+
+ ///
+ /// Releases this instance.
+ ///
+ public void ReleaseDriver()
+ {
+ try
+ {
+ driver.disposeBuffers();
+ } catch (Exception ex)
+ {
+ Console.Out.WriteLine(ex.ToString());
+ }
+ driver.ReleaseComASIODriver();
+ }
+
+ ///
+ /// Determines whether the specified sample rate is supported.
+ ///
+ /// The sample rate.
+ ///
+ /// true if [is sample rate supported]; otherwise, false .
+ ///
+ public bool IsSampleRateSupported(double sampleRate)
+ {
+ return driver.canSampleRate(sampleRate);
+ }
+
+ ///
+ /// Sets the sample rate.
+ ///
+ /// The sample rate.
+ public void SetSampleRate(double sampleRate)
+ {
+ driver.setSampleRate(sampleRate);
+ // Update Capabilities
+ BuildCapabilities();
+ }
+
+ ///
+ /// Gets or sets the fill buffer callback.
+ ///
+ /// The fill buffer callback.
+ public ASIOFillBufferCallback FillBufferCallback
+ {
+ get { return fillBufferCallback; }
+ set { fillBufferCallback = value; }
+ }
+
+ ///
+ /// Gets the capabilities of the ASIODriver.
+ ///
+ /// The capabilities.
+ public AsioDriverCapability Capabilities
+ {
+ get { return capability; }
+ }
+
+ ///
+ /// Creates the buffers for playing.
+ ///
+ /// The number of outputs channels.
+ /// The number of input channel.
+ /// if set to true [use max buffer size] else use Prefered size
+ public int CreateBuffers(int numberOfOutputChannels, int numberOfInputChannels, bool useMaxBufferSize)
+ {
+ if (numberOfOutputChannels < 0 || numberOfOutputChannels > capability.NbOutputChannels)
+ {
+ throw new ArgumentException(String.Format(
+ "Invalid number of channels {0}, must be in the range [0,{1}]",
+ numberOfOutputChannels, capability.NbOutputChannels));
+ }
+ if (numberOfInputChannels < 0 || numberOfInputChannels > capability.NbInputChannels)
+ {
+ throw new ArgumentException("numberOfInputChannels",
+ String.Format("Invalid number of input channels {0}, must be in the range [0,{1}]",
+ numberOfInputChannels, capability.NbInputChannels));
+ }
+
+ // each channel needs a buffer info
+ this.numberOfOutputChannels = numberOfOutputChannels;
+ this.numberOfInputChannels = numberOfInputChannels;
+ // Ask for maximum of output channels even if we use only the nbOutputChannelsArg
+ int nbTotalChannels = capability.NbInputChannels + capability.NbOutputChannels;
+ bufferInfos = new ASIOBufferInfo[nbTotalChannels];
+ currentOutputBuffers = new IntPtr[numberOfOutputChannels];
+ currentInputBuffers = new IntPtr[numberOfInputChannels];
+
+ // and do the same for output channels
+ // ONLY work on output channels (just put isInput = true for InputChannel)
+ int totalIndex = 0;
+ for (int index = 0; index < capability.NbInputChannels; index++, totalIndex++)
+ {
+ bufferInfos[totalIndex].isInput = true;
+ bufferInfos[totalIndex].channelNum = index;
+ bufferInfos[totalIndex].pBuffer0 = IntPtr.Zero;
+ bufferInfos[totalIndex].pBuffer1 = IntPtr.Zero;
+ }
+
+ for (int index = 0; index < capability.NbOutputChannels; index++, totalIndex++)
+ {
+ bufferInfos[totalIndex].isInput = false;
+ bufferInfos[totalIndex].channelNum = index;
+ bufferInfos[totalIndex].pBuffer0 = IntPtr.Zero;
+ bufferInfos[totalIndex].pBuffer1 = IntPtr.Zero;
+ }
+
+ if (useMaxBufferSize)
+ {
+ // use the drivers maximum buffer size
+ bufferSize = capability.BufferMaxSize;
+ }
+ else
+ {
+ // use the drivers preferred buffer size
+ bufferSize = capability.BufferPreferredSize;
+ }
+
+ unsafe
+ {
+ fixed (ASIOBufferInfo* infos = &bufferInfos[0])
+ {
+ IntPtr pOutputBufferInfos = new IntPtr(infos);
+
+ // Create the ASIO Buffers with the callbacks
+ driver.createBuffers(pOutputBufferInfos, nbTotalChannels, bufferSize, ref callbacks);
+ }
+ }
+
+ // Check if outputReady is supported
+ isOutputReadySupported = (driver.outputReady() == ASIOError.ASE_OK);
+ return bufferSize;
+ }
+
+ ///
+ /// Builds the capabilities internally.
+ ///
+ private void BuildCapabilities()
+ {
+ capability = new AsioDriverCapability();
+
+ capability.DriverName = driver.getDriverName();
+
+ // Get nb Input/Output channels
+ driver.getChannels(out capability.NbInputChannels, out capability.NbOutputChannels);
+
+ capability.InputChannelInfos = new ASIOChannelInfo[capability.NbInputChannels];
+ capability.OutputChannelInfos = new ASIOChannelInfo[capability.NbOutputChannels];
+
+ // Get ChannelInfo for Inputs
+ for (int i = 0; i < capability.NbInputChannels; i++)
+ {
+ capability.InputChannelInfos[i] = driver.getChannelInfo(i, true);
+ }
+
+ // Get ChannelInfo for Output
+ for (int i = 0; i < capability.NbOutputChannels; i++)
+ {
+ capability.OutputChannelInfos[i] = driver.getChannelInfo(i, false);
+ }
+
+ // Get the current SampleRate
+ capability.SampleRate = driver.getSampleRate();
+
+ var error = driver.GetLatencies(out capability.InputLatency, out capability.OutputLatency);
+ // focusrite scarlett 2i4 returns ASE_NotPresent here
+
+ if (error != ASIOError.ASE_OK && error != ASIOError.ASE_NotPresent)
+ {
+ var ex = new ASIOException("ASIOgetLatencies");
+ ex.Error = error;
+ throw ex;
+ }
+
+ // Get BufferSize
+ driver.getBufferSize(out capability.BufferMinSize, out capability.BufferMaxSize, out capability.BufferPreferredSize, out capability.BufferGranularity);
+ }
+
+ ///
+ /// Callback called by the ASIODriver on fill buffer demand. Redirect call to external callback.
+ ///
+ /// Index of the double buffer.
+ /// if set to true [direct process].
+ private void BufferSwitchCallBack(int doubleBufferIndex, bool directProcess)
+ {
+ for (int i = 0; i < numberOfInputChannels; i++)
+ {
+ currentInputBuffers[i] = bufferInfos[i + inputChannelOffset].Buffer(doubleBufferIndex);
+ }
+
+ for (int i = 0; i < numberOfOutputChannels; i++)
+ {
+ currentOutputBuffers[i] = bufferInfos[i + outputChannelOffset + capability.NbInputChannels].Buffer(doubleBufferIndex);
+ }
+
+ if (fillBufferCallback != null)
+ {
+ fillBufferCallback(currentInputBuffers, currentOutputBuffers);
+ }
+
+ if (isOutputReadySupported)
+ {
+ driver.outputReady();
+ }
+ }
+
+ ///
+ /// Callback called by the ASIODriver on event "Samples rate changed".
+ ///
+ /// The sample rate.
+ private void SampleRateDidChangeCallBack(double sRate)
+ {
+ // Check when this is called?
+ capability.SampleRate = sRate;
+ }
+
+ ///
+ /// Asio message call back.
+ ///
+ /// The selector.
+ /// The value.
+ /// The message.
+ /// The opt.
+ ///
+ private int AsioMessageCallBack(ASIOMessageSelector selector, int value, IntPtr message, IntPtr opt)
+ {
+ // Check when this is called?
+ switch (selector)
+ {
+ case ASIOMessageSelector.kAsioSelectorSupported:
+ ASIOMessageSelector subValue = (ASIOMessageSelector)Enum.ToObject(typeof(ASIOMessageSelector), value);
+ switch (subValue)
+ {
+ case ASIOMessageSelector.kAsioEngineVersion:
+ return 1;
+ case ASIOMessageSelector.kAsioResetRequest:
+ return 0;
+ case ASIOMessageSelector.kAsioBufferSizeChange:
+ return 0;
+ case ASIOMessageSelector.kAsioResyncRequest:
+ return 0;
+ case ASIOMessageSelector.kAsioLatenciesChanged:
+ return 0;
+ case ASIOMessageSelector.kAsioSupportsTimeInfo:
+// return 1; DON'T SUPPORT FOR NOW. NEED MORE TESTING.
+ return 0;
+ case ASIOMessageSelector.kAsioSupportsTimeCode:
+// return 1; DON'T SUPPORT FOR NOW. NEED MORE TESTING.
+ return 0;
+ }
+ break;
+ case ASIOMessageSelector.kAsioEngineVersion:
+ return 2;
+ case ASIOMessageSelector.kAsioResetRequest:
+ return 1;
+ case ASIOMessageSelector.kAsioBufferSizeChange:
+ return 0;
+ case ASIOMessageSelector.kAsioResyncRequest:
+ return 0;
+ case ASIOMessageSelector.kAsioLatenciesChanged:
+ return 0;
+ case ASIOMessageSelector.kAsioSupportsTimeInfo:
+ return 0;
+ case ASIOMessageSelector.kAsioSupportsTimeCode:
+ return 0;
+ }
+ return 0;
+ }
+
+ ///
+ /// Buffers switch time info call back.
+ ///
+ /// The asio time param.
+ /// Index of the double buffer.
+ /// if set to true [direct process].
+ ///
+ private IntPtr BufferSwitchTimeInfoCallBack(IntPtr asioTimeParam, int doubleBufferIndex, bool directProcess)
+ {
+ // Check when this is called?
+ return IntPtr.Zero;
+ }
+ }
+}
diff --git a/NAudio/Wave/Asio/ASIOSampleConvertor.cs b/NAudio/Wave/Asio/ASIOSampleConvertor.cs
new file mode 100644
index 00000000..ffbe77de
--- /dev/null
+++ b/NAudio/Wave/Asio/ASIOSampleConvertor.cs
@@ -0,0 +1,343 @@
+using System;
+
+namespace NAudio.Wave.Asio
+{
+ ///
+ /// This class stores convertors for different interleaved WaveFormat to ASIOSampleType separate channel
+ /// format.
+ ///
+ internal class ASIOSampleConvertor
+ {
+ public delegate void SampleConvertor(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples);
+
+ ///
+ /// Selects the sample convertor based on the input WaveFormat and the output ASIOSampleTtype.
+ ///
+ /// The wave format.
+ /// The type.
+ ///
+ public static SampleConvertor SelectSampleConvertor(WaveFormat waveFormat, AsioSampleType asioType)
+ {
+ SampleConvertor convertor = null;
+ bool is2Channels = waveFormat.Channels == 2;
+
+ // TODO : IMPLEMENTS OTHER CONVERTOR TYPES
+ switch (asioType)
+ {
+ case AsioSampleType.Int32LSB:
+ switch (waveFormat.BitsPerSample)
+ {
+ case 16:
+ convertor = (is2Channels) ? (SampleConvertor)ConvertorShortToInt2Channels : (SampleConvertor)ConvertorShortToIntGeneric;
+ break;
+ case 32:
+ convertor = (is2Channels) ? (SampleConvertor)ConvertorFloatToInt2Channels : (SampleConvertor)ConvertorFloatToIntGeneric;
+ break;
+ }
+ break;
+ case AsioSampleType.Int16LSB:
+ switch (waveFormat.BitsPerSample)
+ {
+ case 16:
+ convertor = (is2Channels) ? (SampleConvertor)ConvertorShortToShort2Channels : (SampleConvertor)ConvertorShortToShortGeneric;
+ break;
+ case 32:
+ convertor = (is2Channels) ? (SampleConvertor)ConvertorFloatToShort2Channels : (SampleConvertor)ConvertorFloatToShortGeneric;
+ break;
+ }
+ break;
+ case AsioSampleType.Int24LSB:
+ switch (waveFormat.BitsPerSample)
+ {
+ case 16:
+ throw new ArgumentException("Not a supported conversion");
+ case 32:
+ convertor = ConverterFloatTo24LSBGeneric;
+ break;
+ }
+ break;
+ case AsioSampleType.Float32LSB:
+ switch (waveFormat.BitsPerSample)
+ {
+ case 16:
+ throw new ArgumentException("Not a supported conversion");
+ case 32:
+ convertor = ConverterFloatToFloatGeneric;
+ break;
+ }
+ break;
+
+ default:
+ throw new ArgumentException(
+ String.Format("ASIO Buffer Type {0} is not yet supported.",
+ Enum.GetName(typeof(AsioSampleType), asioType)));
+ }
+ return convertor;
+ }
+
+
+ ///
+ /// Optimized convertor for 2 channels SHORT
+ ///
+ public static void ConvertorShortToInt2Channels(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ short* inputSamples = (short*)inputInterleavedBuffer;
+ // Use a trick (short instead of int to avoid any convertion from 16Bit to 32Bit)
+ short* leftSamples = (short*)asioOutputBuffers[0];
+ short* rightSamples = (short*)asioOutputBuffers[1];
+
+ // Point to upper 16 bits of the 32Bits.
+ leftSamples++;
+ rightSamples++;
+ for (int i = 0; i < nbSamples; i++)
+ {
+ *leftSamples = inputSamples[0];
+ *rightSamples = inputSamples[1];
+ // Go to next sample
+ inputSamples += 2;
+ // Add 4 Bytes
+ leftSamples += 2;
+ rightSamples += 2;
+ }
+ }
+ }
+
+ ///
+ /// Generic convertor for SHORT
+ ///
+ public static void ConvertorShortToIntGeneric(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ short* inputSamples = (short*)inputInterleavedBuffer;
+ // Use a trick (short instead of int to avoid any convertion from 16Bit to 32Bit)
+ short*[] samples = new short*[nbChannels];
+ for (int i = 0; i < nbChannels; i++)
+ {
+ samples[i] = (short*)asioOutputBuffers[i];
+ // Point to upper 16 bits of the 32Bits.
+ samples[i]++;
+ }
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ for (int j = 0; j < nbChannels; j++)
+ {
+ *samples[j] = *inputSamples++;
+ samples[j] += 2;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Optimized convertor for 2 channels FLOAT
+ ///
+ public static void ConvertorFloatToInt2Channels(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ float* inputSamples = (float*)inputInterleavedBuffer;
+ int* leftSamples = (int*)asioOutputBuffers[0];
+ int* rightSamples = (int*)asioOutputBuffers[1];
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ *leftSamples++ = clampToInt(inputSamples[0]);
+ *rightSamples++ = clampToInt(inputSamples[1]);
+ inputSamples += 2;
+ }
+ }
+ }
+
+ ///
+ /// Generic convertor SHORT
+ ///
+ public static void ConvertorFloatToIntGeneric(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ float* inputSamples = (float*)inputInterleavedBuffer;
+ int*[] samples = new int*[nbChannels];
+ for (int i = 0; i < nbChannels; i++)
+ {
+ samples[i] = (int*)asioOutputBuffers[i];
+ }
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ for (int j = 0; j < nbChannels; j++)
+ {
+ *samples[j]++ = clampToInt(*inputSamples++);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Optimized convertor for 2 channels SHORT
+ ///
+ public static void ConvertorShortToShort2Channels(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ short* inputSamples = (short*)inputInterleavedBuffer;
+ // Use a trick (short instead of int to avoid any convertion from 16Bit to 32Bit)
+ short* leftSamples = (short*)asioOutputBuffers[0];
+ short* rightSamples = (short*)asioOutputBuffers[1];
+
+ // Point to upper 16 bits of the 32Bits.
+ for (int i = 0; i < nbSamples; i++)
+ {
+ *leftSamples++ = inputSamples[0];
+ *rightSamples++ = inputSamples[1];
+ // Go to next sample
+ inputSamples += 2;
+ }
+ }
+ }
+
+ ///
+ /// Generic convertor for SHORT
+ ///
+ public static void ConvertorShortToShortGeneric(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ short* inputSamples = (short*)inputInterleavedBuffer;
+ // Use a trick (short instead of int to avoid any convertion from 16Bit to 32Bit)
+ short*[] samples = new short*[nbChannels];
+ for (int i = 0; i < nbChannels; i++)
+ {
+ samples[i] = (short*)asioOutputBuffers[i];
+ }
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ for (int j = 0; j < nbChannels; j++)
+ {
+ *(samples[j]++) = *inputSamples++;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Optimized convertor for 2 channels FLOAT
+ ///
+ public static void ConvertorFloatToShort2Channels(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ float* inputSamples = (float*)inputInterleavedBuffer;
+ // Use a trick (short instead of int to avoid any convertion from 16Bit to 32Bit)
+ short* leftSamples = (short*)asioOutputBuffers[0];
+ short* rightSamples = (short*)asioOutputBuffers[1];
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ *leftSamples++ = clampToShort(inputSamples[0]);
+ *rightSamples++ = clampToShort(inputSamples[1]);
+ inputSamples += 2;
+ }
+ }
+ }
+
+ ///
+ /// Generic convertor SHORT
+ ///
+ public static void ConvertorFloatToShortGeneric(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ float* inputSamples = (float*)inputInterleavedBuffer;
+ // Use a trick (short instead of int to avoid any convertion from 16Bit to 32Bit)
+ short*[] samples = new short*[nbChannels];
+ for (int i = 0; i < nbChannels; i++)
+ {
+ samples[i] = (short*)asioOutputBuffers[i];
+ }
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ for (int j = 0; j < nbChannels; j++)
+ {
+ *(samples[j]++) = clampToShort(*inputSamples++);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Generic converter 24 LSB
+ ///
+ public static void ConverterFloatTo24LSBGeneric(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ float* inputSamples = (float*)inputInterleavedBuffer;
+
+ byte*[] samples = new byte*[nbChannels];
+ for (int i = 0; i < nbChannels; i++)
+ {
+ samples[i] = (byte*)asioOutputBuffers[i];
+ }
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ for (int j = 0; j < nbChannels; j++)
+ {
+ int sample24 = clampTo24Bit(*inputSamples++);
+ *(samples[j]++) = (byte)(sample24);
+ *(samples[j]++) = (byte)(sample24 >> 8);
+ *(samples[j]++) = (byte)(sample24 >> 16);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Generic convertor for float
+ ///
+ public static void ConverterFloatToFloatGeneric(IntPtr inputInterleavedBuffer, IntPtr[] asioOutputBuffers, int nbChannels, int nbSamples)
+ {
+ unsafe
+ {
+ float* inputSamples = (float*)inputInterleavedBuffer;
+ float*[] samples = new float*[nbChannels];
+ for (int i = 0; i < nbChannels; i++)
+ {
+ samples[i] = (float*)asioOutputBuffers[i];
+ }
+
+ for (int i = 0; i < nbSamples; i++)
+ {
+ for (int j = 0; j < nbChannels; j++)
+ {
+ *(samples[j]++) = *inputSamples++;
+ }
+ }
+ }
+ }
+
+ private static int clampTo24Bit(double sampleValue)
+ {
+ sampleValue = (sampleValue < -1.0) ? -1.0 : (sampleValue > 1.0) ? 1.0 : sampleValue;
+ return (int)(sampleValue * 8388607.0);
+ }
+
+ private static int clampToInt(double sampleValue)
+ {
+ sampleValue = (sampleValue < -1.0) ? -1.0 : (sampleValue > 1.0) ? 1.0 : sampleValue;
+ return (int)(sampleValue * 2147483647.0);
+ }
+
+ private static short clampToShort(double sampleValue)
+ {
+ sampleValue = (sampleValue < -1.0) ? -1.0 : (sampleValue > 1.0) ? 1.0 : sampleValue;
+ return (short)(sampleValue * 32767.0);
+ }
+ }
+}
diff --git a/NAudio/Wave/Asio/ASIOStructures.cs b/NAudio/Wave/Asio/ASIOStructures.cs
new file mode 100644
index 00000000..2d09af8b
--- /dev/null
+++ b/NAudio/Wave/Asio/ASIOStructures.cs
@@ -0,0 +1,216 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Asio
+{
+ // -------------------------------------------------------------------------------
+ // Structures used by the ASIODriver and ASIODriverExt
+ // -------------------------------------------------------------------------------
+
+ // -------------------------------------------------------------------------------
+ // Error and Exceptions
+ // -------------------------------------------------------------------------------
+ internal enum ASIOError
+ {
+ ASE_OK = 0, // This value will be returned whenever the call succeeded
+ ASE_SUCCESS = 0x3f4847a0, // unique success return value for ASIOFuture calls
+ ASE_NotPresent = -1000, // hardware input or output is not present or available
+ ASE_HWMalfunction, // hardware is malfunctioning (can be returned by any ASIO function)
+ ASE_InvalidParameter, // input parameter invalid
+ ASE_InvalidMode, // hardware is in a bad mode or used in a bad mode
+ ASE_SPNotAdvancing, // hardware is not running when sample position is inquired
+ ASE_NoClock, // sample clock or rate cannot be determined or is not present
+ ASE_NoMemory // not enough memory for completing the request
+ }
+
+ ///
+ /// ASIO common Exception.
+ ///
+ internal class ASIOException : Exception
+ {
+ private ASIOError error;
+
+ public ASIOException()
+ {
+ }
+
+ public ASIOException(string message)
+ : base(message)
+ {
+ }
+
+ public ASIOException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public ASIOError Error
+ {
+ get { return error; }
+ set
+ {
+ error = value;
+ Data["ASIOError"] = error;
+ }
+ }
+
+ ///
+ /// Gets the name of the error.
+ ///
+ /// The error.
+ /// the name of the error
+ static public String getErrorName(ASIOError error)
+ {
+ return Enum.GetName(typeof(ASIOError), error);
+ }
+ }
+
+ // -------------------------------------------------------------------------------
+ // Channel Info, Buffer Info
+ // -------------------------------------------------------------------------------
+
+
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct ASIOChannelInfo
+ {
+ public int channel; // on input, channel index
+ public bool isInput; // on input
+ public bool isActive; // on exit
+ public int channelGroup; // dto
+ [MarshalAs(UnmanagedType.U4)]
+ public AsioSampleType type; // dto
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public String name; // dto
+ };
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ internal struct ASIOBufferInfo
+ {
+ public bool isInput; // on input: ASIOTrue: input, else output
+ public int channelNum; // on input: channel index
+ public IntPtr pBuffer0; // on output: double buffer addresses
+ public IntPtr pBuffer1; // on output: double buffer addresses
+
+ public IntPtr Buffer(int bufferIndex)
+ {
+ return (bufferIndex == 0) ? pBuffer0 : pBuffer1;
+ }
+
+ }
+
+ internal enum ASIOMessageSelector
+ {
+ kAsioSelectorSupported = 1, // selector in , returns 1L if supported,
+ kAsioEngineVersion, // returns engine (host) asio implementation version,
+ kAsioResetRequest, // request driver reset. if accepted, this
+ kAsioBufferSizeChange, // not yet supported, will currently always return 0L.
+ kAsioResyncRequest, // the driver went out of sync, such that
+ kAsioLatenciesChanged, // the drivers latencies have changed. The engine
+ kAsioSupportsTimeInfo, // if host returns true here, it will expect the
+ kAsioSupportsTimeCode, //
+ kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands
+ kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this
+ kAsioSupportsInputGain, // unused and undefined
+ kAsioSupportsInputMeter, // unused and undefined
+ kAsioSupportsOutputGain, // unused and undefined
+ kAsioSupportsOutputMeter, // unused and undefined
+ kAsioOverload, // driver detected an overload
+ }
+
+ // -------------------------------------------------------------------------------
+ // Time structures
+ // -------------------------------------------------------------------------------
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct ASIOTimeCode
+ {
+ public double speed; // speed relation (fraction of nominal speed)
+ // ASIOSamples timeCodeSamples; // time in samples
+ public ASIO64Bit timeCodeSamples; // time in samples
+ public ASIOTimeCodeFlags flags; // some information flags (see below)
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
+ public string future;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ internal struct ASIO64Bit
+ {
+ public uint hi;
+ public uint lo;
+ // TODO: IMPLEMENT AN EASY WAY TO CONVERT THIS TO double AND long
+ };
+
+ [Flags]
+ internal enum ASIOTimeCodeFlags
+ {
+ kTcValid = 1,
+ kTcRunning = 1 << 1,
+ kTcReverse = 1 << 2,
+ kTcOnspeed = 1 << 3,
+ kTcStill = 1 << 4,
+ kTcSpeedValid = 1 << 8
+ };
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct AsioTimeInfo
+ {
+ public double speed; // absolute speed (1. = nominal)
+ public ASIO64Bit systemTime; // system time related to samplePosition, in nanoseconds
+ public ASIO64Bit samplePosition;
+ public double sampleRate; // current rate
+ public AsioTimeInfoFlags flags; // (see below)
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
+ public string reserved;
+ }
+
+ [Flags]
+ internal enum AsioTimeInfoFlags
+ {
+ kSystemTimeValid = 1, // must always be valid
+ kSamplePositionValid = 1 << 1, // must always be valid
+ kSampleRateValid = 1 << 2,
+ kSpeedValid = 1 << 3,
+ kSampleRateChanged = 1 << 4,
+ kClockSourceChanged = 1 << 5
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
+ internal struct ASIOTime
+ { // both input/output
+ public int reserved1;
+ public int reserved2;
+ public int reserved3;
+ public int reserved4;
+ public AsioTimeInfo timeInfo; // required
+ public ASIOTimeCode timeCode; // optional, evaluated if (timeCode.flags & kTcValid)
+ }
+
+ // -------------------------------------------------------------------------------
+ // Callbacks
+ // -------------------------------------------------------------------------------
+
+ [StructLayout(LayoutKind.Sequential, Pack = 4)]
+ internal struct ASIOCallbacks
+ {
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ASIOBufferSwitchCallBack(int doubleBufferIndex, bool directProcess);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void ASIOSampleRateDidChangeCallBack(double sRate);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate int ASIOAsioMessageCallBack(ASIOMessageSelector selector, int value, IntPtr message, IntPtr opt);
+ // return ASIOTime*
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate IntPtr ASIOBufferSwitchTimeInfoCallBack(IntPtr asioTimeParam, int doubleBufferIndex, bool directProcess);
+ // internal delegate IntPtr ASIOBufferSwitchTimeInfoCallBack(ref ASIOTime asioTimeParam, int doubleBufferIndex, bool directProcess);
+
+ // void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess);
+ public ASIOBufferSwitchCallBack pbufferSwitch;
+ // void (*sampleRateDidChange) (ASIOSampleRate sRate);
+ public ASIOSampleRateDidChangeCallBack psampleRateDidChange;
+ // long (*asioMessage) (long selector, long value, void* message, double* opt);
+ public ASIOAsioMessageCallBack pasioMessage;
+ // ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess);
+ public ASIOBufferSwitchTimeInfoCallBack pbufferSwitchTimeInfo;
+ }
+}
diff --git a/NAudio/Wave/Asio/AsioDriverCapability.cs b/NAudio/Wave/Asio/AsioDriverCapability.cs
new file mode 100644
index 00000000..90fd7083
--- /dev/null
+++ b/NAudio/Wave/Asio/AsioDriverCapability.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Asio
+{
+ ///
+ /// ASIODriverCapability holds all the information from the ASIODriver.
+ /// Use ASIODriverExt to get the Capabilities
+ ///
+ internal class AsioDriverCapability
+ {
+ public String DriverName;
+
+ public int NbInputChannels;
+ public int NbOutputChannels;
+
+ public int InputLatency;
+ public int OutputLatency;
+
+ public int BufferMinSize;
+ public int BufferMaxSize;
+ public int BufferPreferredSize;
+ public int BufferGranularity;
+
+ public double SampleRate;
+
+ public ASIOChannelInfo[] InputChannelInfos;
+ public ASIOChannelInfo[] OutputChannelInfos;
+ }
+}
diff --git a/NAudio/Wave/Asio/AsioSampleType.cs b/NAudio/Wave/Asio/AsioSampleType.cs
new file mode 100644
index 00000000..5bda25c4
--- /dev/null
+++ b/NAudio/Wave/Asio/AsioSampleType.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Asio
+{
+ ///
+ /// ASIO Sample Type
+ ///
+ public enum AsioSampleType
+ {
+ ///
+ /// Int 16 MSB
+ ///
+ Int16MSB = 0,
+ ///
+ /// Int 24 MSB (used for 20 bits as well)
+ ///
+ Int24MSB = 1,
+ ///
+ /// Int 32 MSB
+ ///
+ Int32MSB = 2,
+ ///
+ /// IEEE 754 32 bit float
+ ///
+ Float32MSB = 3,
+ ///
+ /// IEEE 754 64 bit double float
+ ///
+ Float64MSB = 4,
+ ///
+ /// 32 bit data with 16 bit alignment
+ ///
+ Int32MSB16 = 8,
+ ///
+ /// 32 bit data with 18 bit alignment
+ ///
+ Int32MSB18 = 9, //
+ ///
+ /// 32 bit data with 20 bit alignment
+ ///
+ Int32MSB20 = 10,
+ ///
+ /// 32 bit data with 24 bit alignment
+ ///
+ Int32MSB24 = 11,
+ ///
+ /// Int 16 LSB
+ ///
+ Int16LSB = 16,
+ ///
+ /// Int 24 LSB
+ /// used for 20 bits as well
+ ///
+ Int24LSB = 17,
+ ///
+ /// Int 32 LSB
+ ///
+ Int32LSB = 18,
+ ///
+ /// IEEE 754 32 bit float, as found on Intel x86 architecture
+ ///
+ Float32LSB = 19,
+ ///
+ /// IEEE 754 64 bit double float, as found on Intel x86 architecture
+ ///
+ Float64LSB = 20,
+ ///
+ /// 32 bit data with 16 bit alignment
+ ///
+ Int32LSB16 = 24,
+ ///
+ /// 32 bit data with 18 bit alignment
+ ///
+ Int32LSB18 = 25,
+ ///
+ /// 32 bit data with 20 bit alignment
+ ///
+ Int32LSB20 = 26,
+ ///
+ /// 32 bit data with 24 bit alignment
+ ///
+ Int32LSB24 = 27,
+ ///
+ /// DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.
+ ///
+ DSDInt8LSB1 = 32,
+ ///
+ /// DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.
+ ///
+ DSDInt8MSB1 = 33,
+ ///
+ /// DSD 8 bit data, 1 sample per byte. No Endianness required.
+ ///
+ DSDInt8NER8 = 40,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmDriver.cs b/NAudio/Wave/Compression/AcmDriver.cs
new file mode 100644
index 00000000..5f6011b7
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmDriver.cs
@@ -0,0 +1,355 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using NAudio.Utils;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Represents an installed ACM Driver
+ ///
+ public class AcmDriver : IDisposable
+ {
+ private static List drivers;
+ private AcmDriverDetails details;
+ private IntPtr driverId;
+ private IntPtr driverHandle;
+ private List formatTags;
+ private List tempFormatsList; // used by enumerator
+ private IntPtr localDllHandle;
+
+ ///
+ /// Helper function to determine whether a particular codec is installed
+ ///
+ /// The short name of the function
+ /// Whether the codec is installed
+ public static bool IsCodecInstalled(string shortName)
+ {
+ foreach (AcmDriver driver in EnumerateAcmDrivers())
+ {
+ if (driver.ShortName == shortName)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Attempts to add a new ACM driver from a file
+ ///
+ /// Full path of the .acm or dll file containing the driver
+ /// Handle to the driver
+ public static AcmDriver AddLocalDriver(string driverFile)
+ {
+ IntPtr handle = NativeMethods.LoadLibrary(driverFile);
+ if (handle == IntPtr.Zero)
+ {
+ throw new ArgumentException("Failed to load driver file");
+ }
+ var driverProc = NativeMethods.GetProcAddress(handle, "DriverProc");
+ if (driverProc == IntPtr.Zero)
+ {
+ NativeMethods.FreeLibrary(handle);
+ throw new ArgumentException("Failed to discover DriverProc");
+ }
+ IntPtr driverHandle;
+ var result = AcmInterop.acmDriverAdd(out driverHandle,
+ handle, driverProc, 0, AcmDriverAddFlags.Function);
+ if (result != MmResult.NoError)
+ {
+ NativeMethods.FreeLibrary(handle);
+ throw new MmException(result, "acmDriverAdd");
+ }
+ var driver = new AcmDriver(driverHandle);
+ // long name seems to be missing when we use acmDriverAdd
+ if (string.IsNullOrEmpty(driver.details.longName))
+ {
+ driver.details.longName = "Local driver: " + Path.GetFileName(driverFile);
+ driver.localDllHandle = handle;
+ }
+ return driver;
+ }
+
+ ///
+ /// Removes a driver previously added using AddLocalDriver
+ ///
+ /// Local driver to remove
+ public static void RemoveLocalDriver(AcmDriver localDriver)
+ {
+ if (localDriver.localDllHandle == IntPtr.Zero)
+ {
+ throw new ArgumentException("Please pass in the AcmDriver returned by the AddLocalDriver method");
+ }
+ var removeResult = AcmInterop.acmDriverRemove(localDriver.driverId, 0); // gets stored as a driver Id
+ NativeMethods.FreeLibrary(localDriver.localDllHandle);
+ MmException.Try(removeResult, "acmDriverRemove");
+ }
+
+ ///
+ /// Show Format Choose Dialog
+ ///
+ /// Owner window handle, can be null
+ /// Window title
+ /// Enumeration flags. None to get everything
+ /// Enumeration format. Only needed with certain enumeration flags
+ /// The selected format
+ /// Textual description of the selected format
+ /// Textual description of the selected format tag
+ /// True if a format was selected
+ public static bool ShowFormatChooseDialog(
+ IntPtr ownerWindowHandle,
+ string windowTitle,
+ AcmFormatEnumFlags enumFlags,
+ WaveFormat enumFormat,
+ out WaveFormat selectedFormat,
+ out string selectedFormatDescription,
+ out string selectedFormatTagDescription)
+ {
+ AcmFormatChoose formatChoose = new AcmFormatChoose();
+ formatChoose.structureSize = Marshal.SizeOf(formatChoose);
+ formatChoose.styleFlags = AcmFormatChooseStyleFlags.None;
+ formatChoose.ownerWindowHandle = ownerWindowHandle;
+ int maxFormatSize = 200; // guess
+ formatChoose.selectedWaveFormatPointer = Marshal.AllocHGlobal(maxFormatSize);
+ formatChoose.selectedWaveFormatByteSize = maxFormatSize;
+ formatChoose.title = windowTitle;
+ formatChoose.name = null;
+ formatChoose.formatEnumFlags = enumFlags;//AcmFormatEnumFlags.None;
+ formatChoose.waveFormatEnumPointer = IntPtr.Zero;
+ if (enumFormat != null)
+ {
+ IntPtr enumPointer = Marshal.AllocHGlobal(Marshal.SizeOf(enumFormat));
+ Marshal.StructureToPtr(enumFormat,enumPointer,false);
+ formatChoose.waveFormatEnumPointer = enumPointer;
+ }
+ formatChoose.instanceHandle = IntPtr.Zero;
+ formatChoose.templateName = null;
+
+ MmResult result = AcmInterop.acmFormatChoose(ref formatChoose);
+ selectedFormat = null;
+ selectedFormatDescription = null;
+ selectedFormatTagDescription = null;
+ if (result == MmResult.NoError)
+ {
+ selectedFormat = WaveFormat.MarshalFromPtr(formatChoose.selectedWaveFormatPointer);
+ selectedFormatDescription = formatChoose.formatDescription;
+ selectedFormatTagDescription = formatChoose.formatTagDescription;
+ }
+
+ Marshal.FreeHGlobal(formatChoose.waveFormatEnumPointer);
+ Marshal.FreeHGlobal(formatChoose.selectedWaveFormatPointer);
+ if(result != MmResult.AcmCancelled && result != MmResult.NoError)
+ {
+ throw new MmException(result, "acmFormatChoose");
+ }
+ return result == MmResult.NoError;
+
+ }
+
+ ///
+ /// Gets the maximum size needed to store a WaveFormat for ACM interop functions
+ ///
+ public int MaxFormatSize
+ {
+ get
+ {
+ int maxFormatSize;
+ MmException.Try(AcmInterop.acmMetrics(driverHandle, AcmMetrics.MaxSizeFormat, out maxFormatSize), "acmMetrics");
+ return maxFormatSize;
+ }
+ }
+
+ ///
+ /// Finds a Driver by its short name
+ ///
+ /// Short Name
+ /// The driver, or null if not found
+ public static AcmDriver FindByShortName(string shortName)
+ {
+ foreach (AcmDriver driver in AcmDriver.EnumerateAcmDrivers())
+ {
+ if (driver.ShortName == shortName)
+ {
+ return driver;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Gets a list of the ACM Drivers installed
+ ///
+ public static IEnumerable EnumerateAcmDrivers()
+ {
+ drivers = new List();
+ MmException.Try(AcmInterop.acmDriverEnum(new AcmInterop.AcmDriverEnumCallback(DriverEnumCallback), IntPtr.Zero, 0), "acmDriverEnum");
+ return drivers;
+ }
+
+ ///
+ /// The callback for acmDriverEnum
+ ///
+ private static bool DriverEnumCallback(IntPtr hAcmDriver, IntPtr dwInstance, AcmDriverDetailsSupportFlags flags)
+ {
+ drivers.Add(new AcmDriver(hAcmDriver));
+ return true;
+ }
+
+ ///
+ /// Creates a new ACM Driver object
+ ///
+ /// Driver handle
+ private AcmDriver(IntPtr hAcmDriver)
+ {
+ driverId = hAcmDriver;
+ details = new AcmDriverDetails();
+ details.structureSize = Marshal.SizeOf(details);
+ MmException.Try(AcmInterop.acmDriverDetails(hAcmDriver, ref details, 0), "acmDriverDetails");
+ }
+
+ ///
+ /// The short name of this driver
+ ///
+ public string ShortName
+ {
+ get
+ {
+ return details.shortName;
+ }
+ }
+
+ ///
+ /// The full name of this driver
+ ///
+ public string LongName
+ {
+ get
+ {
+ return details.longName;
+ }
+ }
+
+ ///
+ /// The driver ID
+ ///
+ public IntPtr DriverId
+ {
+ get
+ {
+ return driverId;
+ }
+ }
+
+ ///
+ /// ToString
+ ///
+ public override string ToString()
+ {
+ return LongName;
+ }
+
+ ///
+ /// The list of FormatTags for this ACM Driver
+ ///
+ public IEnumerable FormatTags
+ {
+ get
+ {
+ if (formatTags == null)
+ {
+ if (driverHandle == IntPtr.Zero)
+ {
+ throw new InvalidOperationException("Driver must be opened first");
+ }
+ formatTags = new List();
+ AcmFormatTagDetails formatTagDetails = new AcmFormatTagDetails();
+ formatTagDetails.structureSize = Marshal.SizeOf(formatTagDetails);
+ MmException.Try(AcmInterop.acmFormatTagEnum(this.driverHandle, ref formatTagDetails, AcmFormatTagEnumCallback, IntPtr.Zero, 0), "acmFormatTagEnum");
+ }
+ return formatTags;
+ }
+ }
+
+
+ ///
+ /// Gets all the supported formats for a given format tag
+ ///
+ /// Format tag
+ /// Supported formats
+ public IEnumerable GetFormats(AcmFormatTag formatTag)
+ {
+ if (driverHandle == IntPtr.Zero)
+ {
+ throw new InvalidOperationException("Driver must be opened first");
+ }
+ tempFormatsList = new List();
+ var formatDetails = new AcmFormatDetails();
+ formatDetails.structSize = Marshal.SizeOf(formatDetails);
+ // need to make sure we have enough space for a waveFormat. formatTag.FormatSize isn't reliable,
+ // and some codecs MaxFormatSize isn't either
+ formatDetails.waveFormatByteSize = 1024;
+ formatDetails.waveFormatPointer = Marshal.AllocHGlobal(formatDetails.waveFormatByteSize);
+ formatDetails.formatTag = (int)formatTag.FormatTag; // (int)WaveFormatEncoding.Unknown
+ var result = AcmInterop.acmFormatEnum(driverHandle,
+ ref formatDetails, AcmFormatEnumCallback, IntPtr.Zero,
+ AcmFormatEnumFlags.None);
+ Marshal.FreeHGlobal(formatDetails.waveFormatPointer);
+ MmException.Try(result,"acmFormatEnum");
+ return tempFormatsList;
+ }
+
+ ///
+ /// Opens this driver
+ ///
+ public void Open()
+ {
+ if (driverHandle == IntPtr.Zero)
+ {
+ MmException.Try(AcmInterop.acmDriverOpen(out driverHandle, DriverId, 0), "acmDriverOpen");
+ }
+ }
+
+ ///
+ /// Closes this driver
+ ///
+ public void Close()
+ {
+ if(driverHandle != IntPtr.Zero)
+ {
+ MmException.Try(AcmInterop.acmDriverClose(driverHandle, 0),"acmDriverClose");
+ driverHandle = IntPtr.Zero;
+ }
+ }
+
+ private bool AcmFormatTagEnumCallback(IntPtr hAcmDriverId, ref AcmFormatTagDetails formatTagDetails, IntPtr dwInstance, AcmDriverDetailsSupportFlags flags)
+ {
+ formatTags.Add(new AcmFormatTag(formatTagDetails));
+ return true;
+ }
+
+ private bool AcmFormatEnumCallback(IntPtr hAcmDriverId, ref AcmFormatDetails formatDetails, IntPtr dwInstance, AcmDriverDetailsSupportFlags flags)
+ {
+ tempFormatsList.Add(new AcmFormat(formatDetails));
+ return true;
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (driverHandle != IntPtr.Zero)
+ {
+ Close();
+ GC.SuppressFinalize(this);
+ }
+ }
+
+ #endregion
+ }
+
+}
diff --git a/NAudio/Wave/Compression/AcmDriverAddFlags.cs b/NAudio/Wave/Compression/AcmDriverAddFlags.cs
new file mode 100644
index 00000000..4be85229
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmDriverAddFlags.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Flags for use with acmDriverAdd
+ ///
+ enum AcmDriverAddFlags
+ {
+ // also ACM_DRIVERADDF_TYPEMASK = 0x00000007;
+
+ ///
+ /// ACM_DRIVERADDF_LOCAL
+ ///
+ Local = 0,
+ ///
+ /// ACM_DRIVERADDF_GLOBAL
+ ///
+ Global = 8,
+ ///
+ /// ACM_DRIVERADDF_FUNCTION
+ ///
+ Function = 3,
+ ///
+ /// ACM_DRIVERADDF_NOTIFYHWND
+ ///
+ NotifyWindowHandle = 4,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmDriverDetails.cs b/NAudio/Wave/Compression/AcmDriverDetails.cs
new file mode 100644
index 00000000..d498de21
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmDriverDetails.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Interop structure for ACM driver details (ACMDRIVERDETAILS)
+ /// http://msdn.microsoft.com/en-us/library/dd742889%28VS.85%29.aspx
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack=2)]
+ struct AcmDriverDetails
+ {
+ ///
+ /// DWORD cbStruct
+ ///
+ public int structureSize;
+ ///
+ /// FOURCC fccType
+ ///
+ public UInt32 fccType;
+ ///
+ /// FOURCC fccComp
+ ///
+ public UInt32 fccComp;
+ ///
+ /// WORD wMid;
+ ///
+ public UInt16 manufacturerId;
+ ///
+ /// WORD wPid
+ ///
+ public UInt16 productId;
+ ///
+ /// DWORD vdwACM
+ ///
+ public UInt32 acmVersion;
+ ///
+ /// DWORD vdwDriver
+ ///
+ public UInt32 driverVersion;
+ ///
+ /// DWORD fdwSupport;
+ ///
+ public AcmDriverDetailsSupportFlags supportFlags;
+ ///
+ /// DWORD cFormatTags
+ ///
+ public int formatTagsCount;
+ ///
+ /// DWORD cFilterTags
+ ///
+ public int filterTagsCount;
+ ///
+ /// HICON hicon
+ ///
+ public IntPtr hicon;
+ ///
+ /// TCHAR szShortName[ACMDRIVERDETAILS_SHORTNAME_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = ShortNameChars)]
+ public string shortName;
+ ///
+ /// TCHAR szLongName[ACMDRIVERDETAILS_LONGNAME_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LongNameChars)]
+ public string longName;
+ ///
+ /// TCHAR szCopyright[ACMDRIVERDETAILS_COPYRIGHT_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CopyrightChars)]
+ public string copyright;
+ ///
+ /// TCHAR szLicensing[ACMDRIVERDETAILS_LICENSING_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LicensingChars)]
+ public string licensing;
+ ///
+ /// TCHAR szFeatures[ACMDRIVERDETAILS_FEATURES_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = FeaturesChars)]
+ public string features;
+
+ ///
+ /// ACMDRIVERDETAILS_SHORTNAME_CHARS
+ ///
+ private const int ShortNameChars = 32;
+ ///
+ /// ACMDRIVERDETAILS_LONGNAME_CHARS
+ ///
+ private const int LongNameChars = 128;
+ ///
+ /// ACMDRIVERDETAILS_COPYRIGHT_CHARS
+ ///
+ private const int CopyrightChars = 80;
+ ///
+ /// ACMDRIVERDETAILS_LICENSING_CHARS
+ ///
+ private const int LicensingChars = 128;
+ ///
+ /// ACMDRIVERDETAILS_FEATURES_CHARS
+ ///
+ private const int FeaturesChars = 512;
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmDriverDetailsSupportFlags.cs b/NAudio/Wave/Compression/AcmDriverDetailsSupportFlags.cs
new file mode 100644
index 00000000..c9c76d26
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmDriverDetailsSupportFlags.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Flags indicating what support a particular ACM driver has
+ ///
+ [Flags]
+ public enum AcmDriverDetailsSupportFlags
+ {
+ /// ACMDRIVERDETAILS_SUPPORTF_CODEC - Codec
+ Codec = 0x00000001,
+ /// ACMDRIVERDETAILS_SUPPORTF_CONVERTER - Converter
+ Converter = 0x00000002,
+ /// ACMDRIVERDETAILS_SUPPORTF_FILTER - Filter
+ Filter = 0x00000004,
+ /// ACMDRIVERDETAILS_SUPPORTF_HARDWARE - Hardware
+ Hardware = 0x00000008,
+ /// ACMDRIVERDETAILS_SUPPORTF_ASYNC - Async
+ Async = 0x00000010,
+ /// ACMDRIVERDETAILS_SUPPORTF_LOCAL - Local
+ Local = 0x40000000,
+ /// ACMDRIVERDETAILS_SUPPORTF_DISABLED - Disabled
+ Disabled = unchecked((int)0x80000000),
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmDriverEnumFlags.cs b/NAudio/Wave/Compression/AcmDriverEnumFlags.cs
new file mode 100644
index 00000000..87761b01
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmDriverEnumFlags.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ [Flags]
+ enum AcmDriverEnumFlags
+ {
+ ///
+ /// ACM_DRIVERENUMF_NOLOCAL, Only global drivers should be included in the enumeration
+ ///
+ NoLocal = 0x40000000,
+ ///
+ /// ACM_DRIVERENUMF_DISABLED, Disabled ACM drivers should be included in the enumeration
+ ///
+ Disabled = unchecked((int)0x80000000),
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormat.cs b/NAudio/Wave/Compression/AcmFormat.cs
new file mode 100644
index 00000000..5a9a6005
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormat.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// ACM Format
+ ///
+ public class AcmFormat
+ {
+ private readonly AcmFormatDetails formatDetails;
+ private readonly WaveFormat waveFormat;
+
+ internal AcmFormat(AcmFormatDetails formatDetails)
+ {
+ this.formatDetails = formatDetails;
+ waveFormat = WaveFormat.MarshalFromPtr(formatDetails.waveFormatPointer);
+ }
+
+ ///
+ /// Format Index
+ ///
+ public int FormatIndex
+ {
+ get { return formatDetails.formatIndex; }
+ }
+
+ ///
+ /// Format Tag
+ ///
+ public WaveFormatEncoding FormatTag
+ {
+ get { return (WaveFormatEncoding)formatDetails.formatTag; }
+ }
+
+ ///
+ /// Support Flags
+ ///
+ public AcmDriverDetailsSupportFlags SupportFlags
+ {
+ get { return formatDetails.supportFlags; }
+ }
+
+ ///
+ /// WaveFormat
+ ///
+ public WaveFormat WaveFormat
+ {
+ get
+ {
+ return waveFormat;
+ }
+ }
+
+ ///
+ /// WaveFormat Size
+ ///
+ public int WaveFormatByteSize
+ {
+ get { return formatDetails.waveFormatByteSize; }
+ }
+
+ ///
+ /// Format Description
+ ///
+ public string FormatDescription
+ {
+ get { return formatDetails.formatDescription; }
+ }
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormatChoose.cs b/NAudio/Wave/Compression/AcmFormatChoose.cs
new file mode 100644
index 00000000..5f014a0b
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormatChoose.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// ACMFORMATCHOOSE
+ /// http://msdn.microsoft.com/en-us/library/dd742911%28VS.85%29.aspx
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
+ struct AcmFormatChoose
+ {
+ ///
+ /// DWORD cbStruct;
+ ///
+ public int structureSize;
+ ///
+ /// DWORD fdwStyle;
+ ///
+ public AcmFormatChooseStyleFlags styleFlags;
+ ///
+ /// HWND hwndOwner;
+ ///
+ public IntPtr ownerWindowHandle;
+ ///
+ /// LPWAVEFORMATEX pwfx;
+ ///
+ public IntPtr selectedWaveFormatPointer;
+ ///
+ /// DWORD cbwfx;
+ ///
+ public int selectedWaveFormatByteSize;
+ ///
+ /// LPCTSTR pszTitle;
+ ///
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string title;
+ ///
+ /// TCHAR szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst=AcmFormatTagDetails.FormatTagDescriptionChars)]
+ public string formatTagDescription;
+ ///
+ /// TCHAR szFormat[ACMFORMATDETAILS_FORMAT_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = AcmFormatDetails.FormatDescriptionChars)]
+ public string formatDescription;
+ ///
+ /// LPTSTR pszName;
+ /// n.b. can be written into
+ ///
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string name;
+ ///
+ /// DWORD cchName
+ /// Should be at least 128 unless name is zero
+ ///
+ public int nameByteSize;
+ ///
+ /// DWORD fdwEnum;
+ ///
+ public AcmFormatEnumFlags formatEnumFlags;
+ ///
+ /// LPWAVEFORMATEX pwfxEnum;
+ ///
+ public IntPtr waveFormatEnumPointer;
+ ///
+ /// HINSTANCE hInstance;
+ ///
+ public IntPtr instanceHandle;
+ ///
+ /// LPCTSTR pszTemplateName;
+ ///
+ [MarshalAs(UnmanagedType.LPTStr)]
+ public string templateName;
+ ///
+ /// LPARAM lCustData;
+ ///
+ public IntPtr customData;
+ ///
+ /// ACMFORMATCHOOSEHOOKPROC pfnHook;
+ ///
+ public AcmInterop.AcmFormatChooseHookProc windowCallbackFunction;
+
+
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormatChooseStyleFlags.cs b/NAudio/Wave/Compression/AcmFormatChooseStyleFlags.cs
new file mode 100644
index 00000000..899309bd
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormatChooseStyleFlags.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ [Flags]
+ enum AcmFormatChooseStyleFlags
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// ACMFORMATCHOOSE_STYLEF_SHOWHELP
+ ///
+ ShowHelp = 0x00000004,
+ ///
+ /// ACMFORMATCHOOSE_STYLEF_ENABLEHOOK
+ ///
+ EnableHook = 0x00000008,
+ ///
+ /// ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATE
+ ///
+ EnableTemplate = 0x00000010,
+ ///
+ /// ACMFORMATCHOOSE_STYLEF_ENABLETEMPLATEHANDLE
+ ///
+ EnableTemplateHandle = 0x00000020,
+ ///
+ /// ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT
+ ///
+ InitToWfxStruct = 0x00000040,
+ ///
+ /// ACMFORMATCHOOSE_STYLEF_CONTEXTHELP
+ ///
+ ContextHelp = 0x00000080
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormatDetails.cs b/NAudio/Wave/Compression/AcmFormatDetails.cs
new file mode 100644
index 00000000..085419cc
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormatDetails.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// ACMFORMATDETAILS
+ /// http://msdn.microsoft.com/en-us/library/dd742913%28VS.85%29.aspx
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack=4)]
+ struct AcmFormatDetails
+ {
+ ///
+ /// DWORD cbStruct;
+ ///
+ public int structSize;
+ ///
+ /// DWORD dwFormatIndex;
+ ///
+ public int formatIndex;
+ ///
+ /// DWORD dwFormatTag;
+ ///
+ public int formatTag;
+ ///
+ /// DWORD fdwSupport;
+ ///
+ public AcmDriverDetailsSupportFlags supportFlags;
+ ///
+ /// LPWAVEFORMATEX pwfx;
+ ///
+ public IntPtr waveFormatPointer;
+ ///
+ /// DWORD cbwfx;
+ ///
+ public int waveFormatByteSize;
+ ///
+ /// TCHAR szFormat[ACMFORMATDETAILS_FORMAT_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = FormatDescriptionChars)]
+ public string formatDescription;
+
+ ///
+ /// ACMFORMATDETAILS_FORMAT_CHARS
+ ///
+ public const int FormatDescriptionChars = 128;
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormatEnumFlags.cs b/NAudio/Wave/Compression/AcmFormatEnumFlags.cs
new file mode 100644
index 00000000..71719c80
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormatEnumFlags.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Format Enumeration Flags
+ ///
+ [Flags]
+ public enum AcmFormatEnumFlags
+ {
+ ///
+ /// None
+ ///
+ None = 0,
+ ///
+ /// ACM_FORMATENUMF_CONVERT
+ /// The WAVEFORMATEX structure pointed to by the pwfx member of the ACMFORMATDETAILS structure is valid. The enumerator will only enumerate destination formats that can be converted from the given pwfx format.
+ ///
+ Convert = 0x00100000,
+ ///
+ /// ACM_FORMATENUMF_HARDWARE
+ /// The enumerator should only enumerate formats that are supported as native input or output formats on one or more of the installed waveform-audio devices. This flag provides a way for an application to choose only formats native to an installed waveform-audio device. This flag must be used with one or both of the ACM_FORMATENUMF_INPUT and ACM_FORMATENUMF_OUTPUT flags. Specifying both ACM_FORMATENUMF_INPUT and ACM_FORMATENUMF_OUTPUT will enumerate only formats that can be opened for input or output. This is true regardless of whether this flag is specified.
+ ///
+ Hardware = 0x00400000,
+ ///
+ /// ACM_FORMATENUMF_INPUT
+ /// Enumerator should enumerate only formats that are supported for input (recording).
+ ///
+ Input = 0x00800000,
+ ///
+ /// ACM_FORMATENUMF_NCHANNELS
+ /// The nChannels member of the WAVEFORMATEX structure pointed to by the pwfx member of the ACMFORMATDETAILS structure is valid. The enumerator will enumerate only a format that conforms to this attribute.
+ ///
+ Channels = 0x00020000,
+ ///
+ /// ACM_FORMATENUMF_NSAMPLESPERSEC
+ /// The nSamplesPerSec member of the WAVEFORMATEX structure pointed to by the pwfx member of the ACMFORMATDETAILS structure is valid. The enumerator will enumerate only a format that conforms to this attribute.
+ ///
+ SamplesPerSecond = 0x00040000,
+ ///
+ /// ACM_FORMATENUMF_OUTPUT
+ /// Enumerator should enumerate only formats that are supported for output (playback).
+ ///
+ Output = 0x01000000,
+ ///
+ /// ACM_FORMATENUMF_SUGGEST
+ /// The WAVEFORMATEX structure pointed to by the pwfx member of the ACMFORMATDETAILS structure is valid. The enumerator will enumerate all suggested destination formats for the given pwfx format. This mechanism can be used instead of the acmFormatSuggest function to allow an application to choose the best suggested format for conversion. The dwFormatIndex member will always be set to zero on return.
+ ///
+ Suggest = 0x00200000,
+ ///
+ /// ACM_FORMATENUMF_WBITSPERSAMPLE
+ /// The wBitsPerSample member of the WAVEFORMATEX structure pointed to by the pwfx member of the ACMFORMATDETAILS structure is valid. The enumerator will enumerate only a format that conforms to this attribute.
+ ///
+ BitsPerSample = 0x00080000,
+ ///
+ /// ACM_FORMATENUMF_WFORMATTAG
+ /// The wFormatTag member of the WAVEFORMATEX structure pointed to by the pwfx member of the ACMFORMATDETAILS structure is valid. The enumerator will enumerate only a format that conforms to this attribute. The dwFormatTag member of the ACMFORMATDETAILS structure must be equal to the wFormatTag member.
+ ///
+ FormatTag = 0x00010000,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormatSuggestFlags.cs b/NAudio/Wave/Compression/AcmFormatSuggestFlags.cs
new file mode 100644
index 00000000..680f8ee9
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormatSuggestFlags.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ [Flags]
+ enum AcmFormatSuggestFlags
+ {
+ ///
+ /// ACM_FORMATSUGGESTF_WFORMATTAG
+ ///
+ FormatTag = 0x00010000,
+ ///
+ /// ACM_FORMATSUGGESTF_NCHANNELS
+ ///
+ Channels = 0x00020000,
+ ///
+ /// ACM_FORMATSUGGESTF_NSAMPLESPERSEC
+ ///
+ SamplesPerSecond = 0x00040000,
+ ///
+ /// ACM_FORMATSUGGESTF_WBITSPERSAMPLE
+ ///
+ BitsPerSample = 0x00080000,
+ ///
+ /// ACM_FORMATSUGGESTF_TYPEMASK
+ ///
+ TypeMask = 0x00FF0000,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormatTag.cs b/NAudio/Wave/Compression/AcmFormatTag.cs
new file mode 100644
index 00000000..df239d61
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormatTag.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// ACM Format Tag
+ ///
+ public class AcmFormatTag
+ {
+ private AcmFormatTagDetails formatTagDetails;
+
+ internal AcmFormatTag(AcmFormatTagDetails formatTagDetails)
+ {
+ this.formatTagDetails = formatTagDetails;
+ }
+
+ ///
+ /// Format Tag Index
+ ///
+ public int FormatTagIndex
+ {
+ get { return formatTagDetails.formatTagIndex; }
+ }
+
+ ///
+ /// Format Tag
+ ///
+ public WaveFormatEncoding FormatTag
+ {
+ get { return (WaveFormatEncoding)formatTagDetails.formatTag; }
+ }
+
+ ///
+ /// Format Size
+ ///
+ public int FormatSize
+ {
+ get { return formatTagDetails.formatSize; }
+ }
+
+ ///
+ /// Support Flags
+ ///
+ public AcmDriverDetailsSupportFlags SupportFlags
+ {
+ get { return formatTagDetails.supportFlags; }
+ }
+
+ ///
+ /// Standard Formats Count
+ ///
+ public int StandardFormatsCount
+ {
+ get { return formatTagDetails.standardFormatsCount; }
+ }
+
+ ///
+ /// Format Description
+ ///
+ public string FormatDescription
+ {
+ get { return formatTagDetails.formatDescription; }
+ }
+
+
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmFormatTagDetails.cs b/NAudio/Wave/Compression/AcmFormatTagDetails.cs
new file mode 100644
index 00000000..d205a8ae
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmFormatTagDetails.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ struct AcmFormatTagDetails
+ {
+ ///
+ /// DWORD cbStruct;
+ ///
+ public int structureSize;
+ ///
+ /// DWORD dwFormatTagIndex;
+ ///
+ public int formatTagIndex;
+ ///
+ /// DWORD dwFormatTag;
+ ///
+ public int formatTag;
+ ///
+ /// DWORD cbFormatSize;
+ ///
+ public int formatSize;
+ ///
+ /// DWORD fdwSupport;
+ ///
+ public AcmDriverDetailsSupportFlags supportFlags;
+ ///
+ /// DWORD cStandardFormats;
+ ///
+ public int standardFormatsCount;
+ ///
+ /// TCHAR szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS];
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = FormatTagDescriptionChars)]
+ public string formatDescription;
+
+
+
+ ///
+ /// ACMFORMATTAGDETAILS_FORMATTAG_CHARS
+ ///
+ public const int FormatTagDescriptionChars = 48;
+
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmInterop.cs b/NAudio/Wave/Compression/AcmInterop.cs
new file mode 100644
index 00000000..18c9f45a
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmInterop.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Interop definitions for Windows ACM (Audio Compression Manager) API
+ ///
+ class AcmInterop
+ {
+ // http://msdn.microsoft.com/en-us/library/dd742891%28VS.85%29.aspx
+ public delegate bool AcmDriverEnumCallback(IntPtr hAcmDriverId, IntPtr instance, AcmDriverDetailsSupportFlags flags);
+
+ public delegate bool AcmFormatEnumCallback(IntPtr hAcmDriverId, ref AcmFormatDetails formatDetails, IntPtr dwInstance, AcmDriverDetailsSupportFlags flags);
+
+ public delegate bool AcmFormatTagEnumCallback(IntPtr hAcmDriverId, ref AcmFormatTagDetails formatTagDetails, IntPtr dwInstance, AcmDriverDetailsSupportFlags flags);
+
+ ///
+ /// http://msdn.microsoft.com/en-us/library/dd742910%28VS.85%29.aspx
+ /// UINT ACMFORMATCHOOSEHOOKPROC acmFormatChooseHookProc(
+ /// HWND hwnd,
+ /// UINT uMsg,
+ /// WPARAM wParam,
+ /// LPARAM lParam
+ ///
+ public delegate bool AcmFormatChooseHookProc(IntPtr windowHandle, int message, IntPtr wParam, IntPtr lParam);
+
+ // not done:
+ // acmDriverAdd
+ // acmDriverID
+ // acmDriverMessage
+ // acmDriverRemove
+ // acmFilterChoose
+ // acmFilterChooseHookProc
+ // acmFilterDetails
+ // acmFilterEnum -acmFilterEnumCallback
+ // acmFilterTagDetails
+ // acmFilterTagEnum
+ // acmFormatDetails
+ // acmFormatTagDetails
+ // acmGetVersion
+ // acmStreamMessage
+
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd742885%28v=vs.85%29.aspx
+ // MMRESULT acmDriverAdd(
+ // LPHACMDRIVERID phadid,
+ // HINSTANCE hinstModule,
+ // LPARAM lParam,
+ // DWORD dwPriority,
+ // DWORD fdwAdd)
+ [DllImport("msacm32.dll")]
+ public static extern MmResult acmDriverAdd(out IntPtr driverHandle,
+ IntPtr driverModule,
+ IntPtr driverFunctionAddress,
+ int priority,
+ AcmDriverAddFlags flags);
+
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd742897%28v=vs.85%29.aspx
+ [DllImport("msacm32.dll")]
+ public static extern MmResult acmDriverRemove(IntPtr driverHandle,
+ int removeFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742886%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmDriverClose(IntPtr hAcmDriver, int closeFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742890%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmDriverEnum(AcmDriverEnumCallback fnCallback, IntPtr dwInstance, AcmDriverEnumFlags flags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742887%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmDriverDetails(IntPtr hAcmDriver, ref AcmDriverDetails driverDetails, int reserved);
+
+ // http://msdn.microsoft.com/en-us/library/dd742894%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmDriverOpen(out IntPtr pAcmDriver, IntPtr hAcmDriverId, int openFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742909%28VS.85%29.aspx
+ [DllImport("Msacm32.dll", EntryPoint = "acmFormatChooseW")]
+ public static extern MmResult acmFormatChoose(ref AcmFormatChoose formatChoose);
+
+ // http://msdn.microsoft.com/en-us/library/dd742914%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmFormatEnum(IntPtr hAcmDriver, ref AcmFormatDetails formatDetails, AcmFormatEnumCallback callback, IntPtr instance, AcmFormatEnumFlags flags);
+
+ ///
+ /// http://msdn.microsoft.com/en-us/library/dd742916%28VS.85%29.aspx
+ /// MMRESULT acmFormatSuggest(
+ /// HACMDRIVER had,
+ /// LPWAVEFORMATEX pwfxSrc,
+ /// LPWAVEFORMATEX pwfxDst,
+ /// DWORD cbwfxDst,
+ /// DWORD fdwSuggest);
+ ///
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmFormatSuggest(
+ IntPtr hAcmDriver,
+ [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NAudio.Wave.WaveFormatCustomMarshaler")]
+ WaveFormat sourceFormat,
+ [In, Out, MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NAudio.Wave.WaveFormatCustomMarshaler")]
+ WaveFormat destFormat,
+ int sizeDestFormat,
+ AcmFormatSuggestFlags suggestFlags);
+
+ [DllImport("Msacm32.dll",EntryPoint="acmFormatSuggest")]
+ public static extern MmResult acmFormatSuggest2(
+ IntPtr hAcmDriver,
+ IntPtr sourceFormatPointer,
+ IntPtr destFormatPointer,
+ int sizeDestFormat,
+ AcmFormatSuggestFlags suggestFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742919%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmFormatTagEnum(IntPtr hAcmDriver, ref AcmFormatTagDetails formatTagDetails, AcmFormatTagEnumCallback callback, IntPtr instance, int reserved);
+
+ // http://msdn.microsoft.com/en-us/library/dd742922%28VS.85%29.aspx
+ // this version of the prototype is for metrics that output a single integer
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmMetrics(IntPtr hAcmObject, AcmMetrics metric, out int output);
+
+ ///
+ /// http://msdn.microsoft.com/en-us/library/dd742928%28VS.85%29.aspx
+ /// MMRESULT acmStreamOpen(
+ /// LPHACMSTREAM phas,
+ /// HACMDRIVER had,
+ /// LPWAVEFORMATEX pwfxSrc,
+ /// LPWAVEFORMATEX pwfxDst,
+ /// LPWAVEFILTER pwfltr,
+ /// DWORD_PTR dwCallback,
+ /// DWORD_PTR dwInstance,
+ /// DWORD fdwOpen
+ ///
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmStreamOpen(
+ out IntPtr hAcmStream,
+ IntPtr hAcmDriver,
+ [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NAudio.Wave.WaveFormatCustomMarshaler")]
+ WaveFormat sourceFormat,
+ [In, MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "NAudio.Wave.WaveFormatCustomMarshaler")]
+ WaveFormat destFormat,
+ [In] WaveFilter waveFilter,
+ IntPtr callback,
+ IntPtr instance,
+ AcmStreamOpenFlags openFlags);
+
+ ///
+ /// A version with pointers for troubleshooting
+ ///
+ [DllImport("Msacm32.dll",EntryPoint="acmStreamOpen")]
+ public static extern MmResult acmStreamOpen2(
+ out IntPtr hAcmStream,
+ IntPtr hAcmDriver,
+ IntPtr sourceFormatPointer,
+ IntPtr destFormatPointer,
+ [In] WaveFilter waveFilter,
+ IntPtr callback,
+ IntPtr instance,
+ AcmStreamOpenFlags openFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742923%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmStreamClose(IntPtr hAcmStream, int closeFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742924%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmStreamConvert(IntPtr hAcmStream, [In, Out] AcmStreamHeaderStruct streamHeader, AcmStreamConvertFlags streamConvertFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742929%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmStreamPrepareHeader(IntPtr hAcmStream, [In, Out] AcmStreamHeaderStruct streamHeader, int prepareFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742929%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmStreamReset(IntPtr hAcmStream, int resetFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742931%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmStreamSize(IntPtr hAcmStream, int inputBufferSize, out int outputBufferSize, AcmStreamSizeFlags flags);
+
+ // http://msdn.microsoft.com/en-us/library/dd742932%28VS.85%29.aspx
+ [DllImport("Msacm32.dll")]
+ public static extern MmResult acmStreamUnprepareHeader(IntPtr hAcmStream, [In, Out] AcmStreamHeaderStruct streamHeader, int flags);
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmMetrics.cs b/NAudio/Wave/Compression/AcmMetrics.cs
new file mode 100644
index 00000000..08d8d5d1
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmMetrics.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ enum AcmMetrics
+ {
+ /// ACM_METRIC_COUNT_DRIVERS
+ CountDrivers = 1,
+ /// ACM_METRIC_COUNT_CODECS
+ CountCodecs = 2,
+ /// ACM_METRIC_COUNT_CONVERTERS
+ CountConverters = 3,
+ /// ACM_METRIC_COUNT_FILTERS
+ CountFilters = 4,
+ /// ACM_METRIC_COUNT_DISABLED
+ CountDisabled = 5,
+ /// ACM_METRIC_COUNT_HARDWARE
+ CountHardware = 6,
+ /// ACM_METRIC_COUNT_LOCAL_DRIVERS
+ CountLocalDrivers = 20,
+ /// ACM_METRIC_COUNT_LOCAL_CODECS
+ CountLocalCodecs = 21,
+ /// ACM_METRIC_COUNT_LOCAL_CONVERTERS
+ CountLocalConverters = 22,
+ /// ACM_METRIC_COUNT_LOCAL_FILTERS
+ CountLocalFilters = 23,
+ /// ACM_METRIC_COUNT_LOCAL_DISABLED
+ CountLocalDisabled = 24,
+ /// ACM_METRIC_HARDWARE_WAVE_INPUT
+ HardwareWaveInput = 30,
+ /// ACM_METRIC_HARDWARE_WAVE_OUTPUT
+ HardwareWaveOutput = 31,
+ /// ACM_METRIC_MAX_SIZE_FORMAT
+ MaxSizeFormat = 50,
+ /// ACM_METRIC_MAX_SIZE_FILTER
+ MaxSizeFilter = 51,
+ /// ACM_METRIC_DRIVER_SUPPORT
+ DriverSupport = 100,
+ /// ACM_METRIC_DRIVER_PRIORITY
+ DriverPriority = 101,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmStream.cs b/NAudio/Wave/Compression/AcmStream.cs
new file mode 100644
index 00000000..b8e541f5
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmStream.cs
@@ -0,0 +1,250 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// AcmStream encapsulates an Audio Compression Manager Stream
+ /// used to convert audio from one format to another
+ ///
+ public class AcmStream : IDisposable
+ {
+ private IntPtr streamHandle;
+ private IntPtr driverHandle;
+ private AcmStreamHeader streamHeader;
+ private WaveFormat sourceFormat;
+
+ ///
+ /// Creates a new ACM stream to convert one format to another. Note that
+ /// not all conversions can be done in one step
+ ///
+ /// The source audio format
+ /// The destination audio format
+ public AcmStream(WaveFormat sourceFormat, WaveFormat destFormat)
+ {
+ try
+ {
+ streamHandle = IntPtr.Zero;
+ this.sourceFormat = sourceFormat;
+ int sourceBufferSize = Math.Max(65536, sourceFormat.AverageBytesPerSecond);
+ sourceBufferSize -= (sourceBufferSize % sourceFormat.BlockAlign);
+ MmException.Try(AcmInterop.acmStreamOpen(out streamHandle, IntPtr.Zero, sourceFormat, destFormat, null, IntPtr.Zero, IntPtr.Zero, AcmStreamOpenFlags.NonRealTime), "acmStreamOpen");
+
+ int destBufferSize = SourceToDest(sourceBufferSize);
+ streamHeader = new AcmStreamHeader(streamHandle, sourceBufferSize, destBufferSize);
+ driverHandle = IntPtr.Zero;
+ }
+ catch
+ {
+ // suppress the finalise and clean up resources
+ Dispose();
+ throw;
+ }
+ }
+
+ ///
+ /// Creates a new ACM stream to convert one format to another, using a
+ /// specified driver identified and wave filter
+ ///
+ /// the driver identifier
+ /// the source format
+ /// the wave filter
+ public AcmStream(IntPtr driverId, WaveFormat sourceFormat, WaveFilter waveFilter)
+ {
+ int sourceBufferSize = Math.Max(16384, sourceFormat.AverageBytesPerSecond);
+ this.sourceFormat = sourceFormat;
+ sourceBufferSize -= (sourceBufferSize % sourceFormat.BlockAlign);
+ MmException.Try(AcmInterop.acmDriverOpen(out driverHandle, driverId, 0), "acmDriverOpen");
+ MmException.Try(AcmInterop.acmStreamOpen(out streamHandle, driverHandle,
+ sourceFormat, sourceFormat, waveFilter, IntPtr.Zero, IntPtr.Zero, AcmStreamOpenFlags.NonRealTime), "acmStreamOpen");
+ streamHeader = new AcmStreamHeader(streamHandle, sourceBufferSize, SourceToDest(sourceBufferSize));
+ }
+
+
+ ///
+ /// Returns the number of output bytes for a given number of input bytes
+ ///
+ /// Number of input bytes
+ /// Number of output bytes
+ public int SourceToDest(int source)
+ {
+ if (source == 0) // zero is an invalid parameter to acmStreamSize
+ return 0;
+ int convertedBytes;
+ var mmResult = AcmInterop.acmStreamSize(streamHandle, source, out convertedBytes, AcmStreamSizeFlags.Source);
+ MmException.Try(mmResult, "acmStreamSize");
+ return convertedBytes;
+ }
+
+ ///
+ /// Returns the number of source bytes for a given number of destination bytes
+ ///
+ /// Number of destination bytes
+ /// Number of source bytes
+ public int DestToSource(int dest)
+ {
+ if (dest == 0) // zero is an invalid parameter to acmStreamSize
+ return 0;
+ int convertedBytes;
+ MmException.Try(AcmInterop.acmStreamSize(streamHandle, dest, out convertedBytes, AcmStreamSizeFlags.Destination), "acmStreamSize");
+ return convertedBytes;
+ }
+
+ ///
+ /// Suggests an appropriate PCM format that the compressed format can be converted
+ /// to in one step
+ ///
+ /// The compressed format
+ /// The PCM format
+ public static WaveFormat SuggestPcmFormat(WaveFormat compressedFormat)
+ {
+ // create a PCM format
+ WaveFormat suggestedFormat = new WaveFormat(compressedFormat.SampleRate, 16, compressedFormat.Channels);
+ MmException.Try(AcmInterop.acmFormatSuggest(IntPtr.Zero, compressedFormat, suggestedFormat, Marshal.SizeOf(suggestedFormat), AcmFormatSuggestFlags.FormatTag), "acmFormatSuggest");
+
+ /*IntPtr suggestedFormatPointer = WaveFormat.MarshalToPtr(suggestedFormat);
+ IntPtr compressedFormatPointer = WaveFormat.MarshalToPtr(compressedFormat);
+ MmResult result = AcmInterop.acmFormatSuggest2(IntPtr.Zero, compressedFormatPointer, suggestedFormatPointer, Marshal.SizeOf(suggestedFormat), AcmFormatSuggestFlags.FormatTag);
+ suggestedFormat = WaveFormat.MarshalFromPtr(suggestedFormatPointer);
+ Marshal.FreeHGlobal(suggestedFormatPointer);
+ Marshal.FreeHGlobal(compressedFormatPointer);
+ MmException.Try(result, "acmFormatSuggest");*/
+
+
+ return suggestedFormat;
+ }
+
+ ///
+ /// Returns the Source Buffer. Fill this with data prior to calling convert
+ ///
+ public byte[] SourceBuffer
+ {
+ get
+ {
+ return streamHeader.SourceBuffer;
+ }
+ }
+
+ ///
+ /// Returns the Destination buffer. This will contain the converted data
+ /// after a successful call to Convert
+ ///
+ public byte[] DestBuffer
+ {
+ get
+ {
+ return streamHeader.DestBuffer;
+ }
+ }
+
+ ///
+ /// Report that we have repositioned in the source stream
+ ///
+ public void Reposition()
+ {
+ streamHeader.Reposition();
+ }
+
+ ///
+ /// Converts the contents of the SourceBuffer into the DestinationBuffer
+ ///
+ /// The number of bytes in the SourceBuffer
+ /// that need to be converted
+ /// The number of source bytes actually converted
+ /// The number of converted bytes in the DestinationBuffer
+ public int Convert(int bytesToConvert, out int sourceBytesConverted)
+ {
+ if (bytesToConvert % sourceFormat.BlockAlign != 0)
+ {
+ System.Diagnostics.Debug.WriteLine(String.Format("Not a whole number of blocks: {0} ({1})", bytesToConvert, sourceFormat.BlockAlign));
+ bytesToConvert -= (bytesToConvert % sourceFormat.BlockAlign);
+ }
+
+ return streamHeader.Convert(bytesToConvert, out sourceBytesConverted);
+ }
+
+ ///
+ /// Converts the contents of the SourceBuffer into the DestinationBuffer
+ ///
+ /// The number of bytes in the SourceBuffer
+ /// that need to be converted
+ /// The number of converted bytes in the DestinationBuffer
+ [Obsolete("Call the version returning sourceBytesConverted instead")]
+ public int Convert(int bytesToConvert)
+ {
+ int sourceBytesConverted;
+ int destBytes = Convert(bytesToConvert, out sourceBytesConverted);
+ if (sourceBytesConverted != bytesToConvert)
+ {
+ throw new MmException(MmResult.NotSupported, "AcmStreamHeader.Convert didn't convert everything");
+ }
+ return destBytes;
+ }
+
+ /* Relevant only for async conversion streams
+ public void Reset()
+ {
+ MmException.Try(AcmInterop.acmStreamReset(streamHandle,0),"acmStreamReset");
+ }
+ */
+
+ #region IDisposable Members
+
+ ///
+ /// Frees resources associated with this ACM Stream
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Frees resources associated with this ACM Stream
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Free other state (managed objects).
+ if (streamHeader != null)
+ {
+ streamHeader.Dispose();
+ streamHeader = null;
+ }
+ }
+
+ // Free your own state (unmanaged objects).
+
+ if (streamHandle != IntPtr.Zero)
+ {
+ MmResult result = AcmInterop.acmStreamClose(streamHandle, 0);
+ streamHandle = IntPtr.Zero;
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "acmStreamClose");
+ }
+
+ }
+ // Set large fields to null.
+ if (driverHandle != IntPtr.Zero)
+ {
+ AcmInterop.acmDriverClose(driverHandle, 0);
+ driverHandle = IntPtr.Zero;
+ }
+ }
+
+ ///
+ /// Frees resources associated with this ACM Stream
+ ///
+ ~AcmStream()
+ {
+ // Simply call Dispose(false).
+ System.Diagnostics.Debug.Assert(false, "AcmStream Dispose was not called");
+ Dispose(false);
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmStreamConvertFlags.cs b/NAudio/Wave/Compression/AcmStreamConvertFlags.cs
new file mode 100644
index 00000000..f11208ac
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmStreamConvertFlags.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ [Flags]
+ enum AcmStreamConvertFlags
+ {
+ ///
+ /// ACM_STREAMCONVERTF_BLOCKALIGN
+ ///
+ BlockAlign = 0x00000004,
+ ///
+ /// ACM_STREAMCONVERTF_START
+ ///
+ Start = 0x00000010,
+ ///
+ /// ACM_STREAMCONVERTF_END
+ ///
+ End = 0x00000020,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmStreamHeader.cs b/NAudio/Wave/Compression/AcmStreamHeader.cs
new file mode 100644
index 00000000..497bfb8f
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmStreamHeader.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ class AcmStreamHeader : IDisposable
+ {
+ private AcmStreamHeaderStruct streamHeader;
+ private byte[] sourceBuffer;
+ private GCHandle hSourceBuffer;
+ private byte[] destBuffer;
+ private GCHandle hDestBuffer;
+ private IntPtr streamHandle;
+ private bool firstTime;
+
+ public AcmStreamHeader(IntPtr streamHandle, int sourceBufferLength, int destBufferLength)
+ {
+ streamHeader = new AcmStreamHeaderStruct();
+ sourceBuffer = new byte[sourceBufferLength];
+ hSourceBuffer = GCHandle.Alloc(sourceBuffer, GCHandleType.Pinned);
+
+ destBuffer = new byte[destBufferLength];
+ hDestBuffer = GCHandle.Alloc(destBuffer, GCHandleType.Pinned);
+
+ this.streamHandle = streamHandle;
+ firstTime = true;
+ //Prepare();
+ }
+
+ private void Prepare()
+ {
+ streamHeader.cbStruct = Marshal.SizeOf(streamHeader);
+ streamHeader.sourceBufferLength = sourceBuffer.Length;
+ streamHeader.sourceBufferPointer = hSourceBuffer.AddrOfPinnedObject();
+ streamHeader.destBufferLength = destBuffer.Length;
+ streamHeader.destBufferPointer = hDestBuffer.AddrOfPinnedObject();
+ MmException.Try(AcmInterop.acmStreamPrepareHeader(streamHandle, streamHeader, 0), "acmStreamPrepareHeader");
+ }
+
+ private void Unprepare()
+ {
+ streamHeader.sourceBufferLength = sourceBuffer.Length;
+ streamHeader.sourceBufferPointer = hSourceBuffer.AddrOfPinnedObject();
+ streamHeader.destBufferLength = destBuffer.Length;
+ streamHeader.destBufferPointer = hDestBuffer.AddrOfPinnedObject();
+
+ MmResult result = AcmInterop.acmStreamUnprepareHeader(streamHandle, streamHeader, 0);
+ if (result != MmResult.NoError)
+ {
+ //if (result == MmResult.AcmHeaderUnprepared)
+ throw new MmException(result, "acmStreamUnprepareHeader");
+ }
+ }
+
+ public void Reposition()
+ {
+ firstTime = true;
+ }
+
+ public int Convert(int bytesToConvert, out int sourceBytesConverted)
+ {
+ Prepare();
+ try
+ {
+ streamHeader.sourceBufferLength = bytesToConvert;
+ streamHeader.sourceBufferLengthUsed = bytesToConvert;
+ AcmStreamConvertFlags flags = firstTime ? (AcmStreamConvertFlags.Start | AcmStreamConvertFlags.BlockAlign) : AcmStreamConvertFlags.BlockAlign;
+ MmException.Try(AcmInterop.acmStreamConvert(streamHandle, streamHeader, flags), "acmStreamConvert");
+ firstTime = false;
+ System.Diagnostics.Debug.Assert(streamHeader.destBufferLength == destBuffer.Length, "Codecs should not change dest buffer length");
+ sourceBytesConverted = streamHeader.sourceBufferLengthUsed;
+ }
+ finally
+ {
+ Unprepare();
+ }
+
+ return streamHeader.destBufferLengthUsed;
+ }
+
+ public byte[] SourceBuffer
+ {
+ get
+ {
+ return sourceBuffer;
+ }
+ }
+
+ public byte[] DestBuffer
+ {
+ get
+ {
+ return destBuffer;
+ }
+ }
+
+ #region IDisposable Members
+
+ bool disposed = false;
+
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposed)
+ {
+ //Unprepare();
+ sourceBuffer = null;
+ destBuffer = null;
+ hSourceBuffer.Free();
+ hDestBuffer.Free();
+ }
+ disposed = true;
+ }
+
+ ~AcmStreamHeader()
+ {
+ System.Diagnostics.Debug.Assert(false, "AcmStreamHeader dispose was not called");
+ Dispose(false);
+ }
+ #endregion
+ }
+
+}
diff --git a/NAudio/Wave/Compression/AcmStreamHeaderStatusFlags.cs b/NAudio/Wave/Compression/AcmStreamHeaderStatusFlags.cs
new file mode 100644
index 00000000..e2fd8f51
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmStreamHeaderStatusFlags.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ [Flags]
+ enum AcmStreamHeaderStatusFlags
+ {
+ ///
+ /// ACMSTREAMHEADER_STATUSF_DONE
+ ///
+ Done = 0x00010000,
+ ///
+ /// ACMSTREAMHEADER_STATUSF_PREPARED
+ ///
+ Prepared = 0x00020000,
+ ///
+ /// ACMSTREAMHEADER_STATUSF_INQUEUE
+ ///
+ InQueue = 0x00100000,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmStreamHeaderStruct.cs b/NAudio/Wave/Compression/AcmStreamHeaderStruct.cs
new file mode 100644
index 00000000..99645097
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmStreamHeaderStruct.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Interop structure for ACM stream headers.
+ /// ACMSTREAMHEADER
+ /// http://msdn.microsoft.com/en-us/library/dd742926%28VS.85%29.aspx
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 128)] // explicit size to make it work for x64
+ class AcmStreamHeaderStruct
+ {
+ public int cbStruct;
+ public AcmStreamHeaderStatusFlags fdwStatus = 0;
+ public IntPtr userData;
+ public IntPtr sourceBufferPointer;
+ public int sourceBufferLength;
+ public int sourceBufferLengthUsed;
+ public IntPtr sourceUserData;
+ public IntPtr destBufferPointer;
+ public int destBufferLength;
+ public int destBufferLengthUsed = 0;
+ public IntPtr destUserData;
+
+ // 10 reserved values follow this, we don't need to declare them
+ // since we have set the struct size explicitly and don't
+ // need to access them in client code (thanks Brian)
+ /*public int reserved0;
+ public int reserved1;
+ public int reserved2;
+ public int reserved3;
+ public int reserved4;
+ public int reserved5;
+ public int reserved6;
+ public int reserved7;
+ public int reserved8;
+ public int reserved9;*/
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmStreamOpenFlags.cs b/NAudio/Wave/Compression/AcmStreamOpenFlags.cs
new file mode 100644
index 00000000..a48343bb
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmStreamOpenFlags.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ [Flags]
+ enum AcmStreamOpenFlags
+ {
+ ///
+ /// ACM_STREAMOPENF_QUERY, ACM will be queried to determine whether it supports the given conversion. A conversion stream will not be opened, and no handle will be returned in the phas parameter.
+ ///
+ Query = 0x00000001,
+ ///
+ /// ACM_STREAMOPENF_ASYNC, Stream conversion should be performed asynchronously. If this flag is specified, the application can use a callback function to be notified when the conversion stream is opened and closed and after each buffer is converted. In addition to using a callback function, an application can examine the fdwStatus member of the ACMSTREAMHEADER structure for the ACMSTREAMHEADER_STATUSF_DONE flag.
+ ///
+ Async = 0x00000002,
+ ///
+ /// ACM_STREAMOPENF_NONREALTIME, ACM will not consider time constraints when converting the data. By default, the driver will attempt to convert the data in real time. For some formats, specifying this flag might improve the audio quality or other characteristics.
+ ///
+ NonRealTime = 0x00000004,
+ ///
+ /// CALLBACK_TYPEMASK, callback type mask
+ ///
+ CallbackTypeMask = 0x00070000,
+ ///
+ /// CALLBACK_NULL, no callback
+ ///
+ CallbackNull = 0x00000000,
+ ///
+ /// CALLBACK_WINDOW, dwCallback is a HWND
+ ///
+ CallbackWindow = 0x00010000,
+ ///
+ /// CALLBACK_TASK, dwCallback is a HTASK
+ ///
+ CallbackTask = 0x00020000,
+ ///
+ /// CALLBACK_FUNCTION, dwCallback is a FARPROC
+ ///
+ CallbackFunction = 0x00030000,
+ ///
+ /// CALLBACK_THREAD, thread ID replaces 16 bit task
+ ///
+ CallbackThread = CallbackTask,
+ ///
+ /// CALLBACK_EVENT, dwCallback is an EVENT Handle
+ ///
+ CallbackEvent = 0x00050000,
+ }
+}
diff --git a/NAudio/Wave/Compression/AcmStreamSizeFlags.cs b/NAudio/Wave/Compression/AcmStreamSizeFlags.cs
new file mode 100644
index 00000000..b18dbbfe
--- /dev/null
+++ b/NAudio/Wave/Compression/AcmStreamSizeFlags.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.Compression
+{
+ enum AcmStreamSizeFlags
+ {
+ ///
+ /// ACM_STREAMSIZEF_SOURCE
+ ///
+ Source = 0x00000000,
+ ///
+ /// ACM_STREAMSIZEF_DESTINATION
+ ///
+ Destination = 0x00000001
+ }
+}
diff --git a/NAudio/Wave/Compression/WaveFilter.cs b/NAudio/Wave/Compression/WaveFilter.cs
new file mode 100644
index 00000000..ec99389d
--- /dev/null
+++ b/NAudio/Wave/Compression/WaveFilter.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.Compression
+{
+ ///
+ /// Summary description for WaveFilter.
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
+ public class WaveFilter
+ {
+ ///
+ /// cbStruct
+ ///
+ public int StructureSize = Marshal.SizeOf(typeof(WaveFilter));
+ ///
+ /// dwFilterTag
+ ///
+ public int FilterTag = 0;
+ ///
+ /// fdwFilter
+ ///
+ public int Filter = 0;
+ ///
+ /// reserved
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst=5)]
+ public int []Reserved = null;
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/Manufacturers.cs b/NAudio/Wave/MmeInterop/Manufacturers.cs
new file mode 100644
index 00000000..7d9be1b4
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/Manufacturers.cs
@@ -0,0 +1,376 @@
+using System;
+
+namespace NAudio
+{
+ ///
+ /// Manufacturer codes from mmreg.h
+ ///
+ public enum Manufacturers
+ {
+ /// Microsoft Corporation
+ Microsoft = 1,
+ /// Creative Labs, Inc
+ Creative = 2,
+ /// Media Vision, Inc.
+ Mediavision = 3,
+ /// Fujitsu Corp.
+ Fujitsu = 4,
+ /// Artisoft, Inc.
+ Artisoft = 20,
+ /// Turtle Beach, Inc.
+ TurtleBeach = 21,
+ /// IBM Corporation
+ Ibm = 22,
+ /// Vocaltec LTD.
+ Vocaltec = 23,
+ /// Roland
+ Roland = 24,
+ /// DSP Solutions, Inc.
+ DspSolutions = 25,
+ /// NEC
+ Nec = 26,
+ /// ATI
+ Ati = 27,
+ /// Wang Laboratories, Inc
+ Wanglabs = 28,
+ /// Tandy Corporation
+ Tandy = 29,
+ /// Voyetra
+ Voyetra = 30,
+ /// Antex Electronics Corporation
+ Antex = 31,
+ /// ICL Personal Systems
+ IclPS = 32,
+ /// Intel Corporation
+ Intel = 33,
+ /// Advanced Gravis
+ Gravis = 34,
+ /// Video Associates Labs, Inc.
+ Val = 35,
+ /// InterActive Inc
+ Interactive = 36,
+ /// Yamaha Corporation of America
+ Yamaha = 37,
+ /// Everex Systems, Inc
+ Everex = 38,
+ /// Echo Speech Corporation
+ Echo = 39,
+ /// Sierra Semiconductor Corp
+ Sierra = 40,
+ /// Computer Aided Technologies
+ Cat = 41,
+ /// APPS Software International
+ Apps = 42,
+ /// DSP Group, Inc
+ DspGroup = 43,
+ /// microEngineering Labs
+ Melabs = 44,
+ /// Computer Friends, Inc.
+ ComputerFriends = 45,
+ /// ESS Technology
+ Ess = 46,
+ /// Audio, Inc.
+ Audiofile = 47,
+ /// Motorola, Inc.
+ Motorola = 48,
+ /// Canopus, co., Ltd.
+ Canopus = 49,
+ /// Seiko Epson Corporation
+ Epson = 50,
+ /// Truevision
+ Truevision = 51,
+ /// Aztech Labs, Inc.
+ Aztech = 52,
+ /// Videologic
+ Videologic = 53,
+ /// SCALACS
+ Scalacs = 54,
+ /// Korg Inc.
+ Korg = 55,
+ /// Audio Processing Technology
+ Apt = 56,
+ /// Integrated Circuit Systems, Inc.
+ Ics = 57,
+ /// Iterated Systems, Inc.
+ Iteratedsys = 58,
+ /// Metheus
+ Metheus = 59,
+ /// Logitech, Inc.
+ Logitech = 60,
+ /// Winnov, Inc.
+ Winnov = 61,
+ /// NCR Corporation
+ Ncr = 62,
+ /// EXAN
+ Exan = 63,
+ /// AST Research Inc.
+ Ast = 64,
+ /// Willow Pond Corporation
+ Willowpond = 65,
+ /// Sonic Foundry
+ Sonicfoundry = 66,
+ /// Vitec Multimedia
+ Vitec = 67,
+ /// MOSCOM Corporation
+ Moscom = 68,
+ /// Silicon Soft, Inc.
+ Siliconsoft = 69,
+ /// Supermac
+ Supermac = 73,
+ /// Audio Processing Technology
+ Audiopt = 74,
+ /// Speech Compression
+ Speechcomp = 76,
+ /// Ahead, Inc.
+ Ahead = 77,
+ /// Dolby Laboratories
+ Dolby = 78,
+ /// OKI
+ Oki = 79,
+ /// AuraVision Corporation
+ Auravision = 80,
+ /// Ing C. Olivetti & C., S.p.A.
+ Olivetti = 81,
+ /// I/O Magic Corporation
+ Iomagic = 82,
+ /// Matsushita Electric Industrial Co., LTD.
+ Matsushita = 83,
+ /// Control Resources Limited
+ Controlres = 84,
+ /// Xebec Multimedia Solutions Limited
+ Xebec = 85,
+ /// New Media Corporation
+ Newmedia = 86,
+ /// Natural MicroSystems
+ Nms = 87,
+ /// Lyrrus Inc.
+ Lyrrus = 88,
+ /// Compusic
+ Compusic = 89,
+ /// OPTi Computers Inc.
+ Opti = 90,
+ /// Adlib Accessories Inc.
+ Adlacc = 91,
+ /// Compaq Computer Corp.
+ Compaq = 92,
+ /// Dialogic Corporation
+ Dialogic = 93,
+ /// InSoft, Inc.
+ Insoft = 94,
+ /// M.P. Technologies, Inc.
+ Mptus = 95,
+ /// Weitek
+ Weitek = 96,
+ /// Lernout & Hauspie
+ LernoutAndHauspie = 97,
+ /// Quanta Computer Inc.
+ Qciar = 98,
+ /// Apple Computer, Inc.
+ Apple = 99,
+ /// Digital Equipment Corporation
+ Digital = 100,
+ /// Mark of the Unicorn
+ Motu = 101,
+ /// Workbit Corporation
+ Workbit = 102,
+ /// Ositech Communications Inc.
+ Ositech = 103,
+ /// miro Computer Products AG
+ Miro = 104,
+ /// Cirrus Logic
+ Cirruslogic = 105,
+ /// ISOLUTION B.V.
+ Isolution = 106,
+ /// Horizons Technology, Inc
+ Horizons = 107,
+ /// Computer Concepts Ltd
+ Concepts = 108,
+ /// Voice Technologies Group, Inc.
+ Vtg = 109,
+ /// Radius
+ Radius = 110,
+ /// Rockwell International
+ Rockwell = 111,
+ /// Co. XYZ for testing
+ Xyz = 112,
+ /// Opcode Systems
+ Opcode = 113,
+ /// Voxware Inc
+ Voxware = 114,
+ /// Northern Telecom Limited
+ NorthernTelecom = 115,
+ /// APICOM
+ Apicom = 116,
+ /// Grande Software
+ Grande = 117,
+ /// ADDX
+ Addx = 118,
+ /// Wildcat Canyon Software
+ Wildcat = 119,
+ /// Rhetorex Inc
+ Rhetorex = 120,
+ /// Brooktree Corporation
+ Brooktree = 121,
+ /// ENSONIQ Corporation
+ Ensoniq = 125,
+ /// FAST Multimedia AG
+ Fast = 126,
+ /// NVidia Corporation
+ Nvidia = 127,
+ /// OKSORI Co., Ltd.
+ Oksori = 128,
+ /// DiAcoustics, Inc.
+ Diacoustics = 129,
+ /// Gulbransen, Inc.
+ Gulbransen = 130,
+ /// Kay Elemetrics, Inc.
+ KayElemetrics = 131,
+ /// Crystal Semiconductor Corporation
+ Crystal = 132,
+ /// Splash Studios
+ SplashStudios = 133,
+ /// Quarterdeck Corporation
+ Quarterdeck = 134,
+ /// TDK Corporation
+ Tdk = 135,
+ /// Digital Audio Labs, Inc.
+ DigitalAudioLabs = 136,
+ /// Seer Systems, Inc.
+ Seersys = 137,
+ /// PictureTel Corporation
+ Picturetel = 138,
+ /// AT&T Microelectronics
+ AttMicroelectronics = 139,
+ /// Osprey Technologies, Inc.
+ Osprey = 140,
+ /// Mediatrix Peripherals
+ Mediatrix = 141,
+ /// SounDesignS M.C.S. Ltd.
+ Soundesigns = 142,
+ /// A.L. Digital Ltd.
+ Aldigital = 143,
+ /// Spectrum Signal Processing, Inc.
+ SpectrumSignalProcessing = 144,
+ /// Electronic Courseware Systems, Inc.
+ Ecs = 145,
+ /// AMD
+ Amd = 146,
+ /// Core Dynamics
+ Coredynamics = 147,
+ /// CANAM Computers
+ Canam = 148,
+ /// Softsound, Ltd.
+ Softsound = 149,
+ /// Norris Communications, Inc.
+ Norris = 150,
+ /// Danka Data Devices
+ Ddd = 151,
+ /// EuPhonics
+ Euphonics = 152,
+ /// Precept Software, Inc.
+ Precept = 153,
+ /// Crystal Net Corporation
+ CrystalNet = 154,
+ /// Chromatic Research, Inc
+ Chromatic = 155,
+ /// Voice Information Systems, Inc
+ Voiceinfo = 156,
+ /// Vienna Systems
+ Viennasys = 157,
+ /// Connectix Corporation
+ Connectix = 158,
+ /// Gadget Labs LLC
+ Gadgetlabs = 159,
+ /// Frontier Design Group LLC
+ Frontier = 160,
+ /// Viona Development GmbH
+ Viona = 161,
+ /// Casio Computer Co., LTD
+ Casio = 162,
+ /// Diamond Multimedia
+ Diamondmm = 163,
+ /// S3
+ S3 = 164,
+ /// Fraunhofer
+ FraunhoferIis = 172,
+
+ /*
+ public static String GetName(int manufacturerId) {
+ switch(manufacturerId) {
+ case Gravis: return "Advanced Gravis Computer Technology, Ltd.";
+ case Antex: return "Antex Electronics Corporation";
+ case Apps: return "APPS Software";
+ case Artisoft: return "Artisoft, Inc.";
+ case Ast: return "AST Research, Inc.";
+ case Ati: return "ATI Technologies, Inc.";
+ case Audiofile: return "Audio, Inc.";
+ case Apt: return "Audio Processing Technology";
+ case Audiopt: return "Audio Processing Technology";
+ case Auravision: return "Auravision Corporation";
+ case Aztech: return "Aztech Labs, Inc.";
+ case Canopus: return "Canopus, Co., Ltd.";
+ case Compusic: return "Compusic";
+ case Cat: return "Computer Aided Technology, Inc.";
+ case ComputerFriends: return "Computer Friends, Inc.";
+ case Controlres: return "Control Resources Corporation";
+ case Creative: return "Creative Labs, Inc.";
+ case Dialogic: return "Dialogic Corporation";
+ case Dolby: return "Dolby Laboratories, Inc.";
+ case DspGroup: return "DSP Group, Inc.";
+ case DspSolutions: return "DSP Solutions, Inc.";
+ case Echo: return "Echo Speech Corporation";
+ case Ess: return "ESS Technology, Inc.";
+ case Everex: return "Everex Systems, Inc.";
+ case Exan: return "EXAN, Ltd.";
+ case Fujitsu: return "Fujitsu, Ltd.";
+ case Iomagic: return "I/O Magic Corporation";
+ case IclPS: return "ICL Personal Systems";
+ case Olivetti: return "Ing. C. Olivetti & C., S.p.A.";
+ case Ics: return "Integrated Circuit Systems, Inc.";
+ case Intel: return "Intel Corporation";
+ case Interactive: return "InterActive, Inc.";
+ case Ibm: return "International Business Machines";
+ case Iteratedsys: return "Iterated Systems, Inc.";
+ case Logitech: return "Logitech, Inc.";
+ case Lyrrus: return "Lyrrus, Inc.";
+ case Matsushita: return "Matsushita Electric Corporation of America";
+ case Mediavision: return "Media Vision, Inc.";
+ case Metheus: return "Metheus Corporation";
+ case Melabs: return "microEngineering Labs";
+ case Microsoft: return "Microsoft Corporation";
+ case Moscom: return "MOSCOM Corporation";
+ case Motorola: return "Motorola, Inc.";
+ case Nms: return "Natural MicroSystems Corporation";
+ case Ncr: return "NCR Corporation";
+ case Nec: return "NEC Corporation";
+ case Newmedia: return "New Media Corporation";
+ case Oki: return "OKI";
+ case Opti: return "OPTi, Inc.";
+ case Roland: return "Roland Corporation";
+ case Scalacs: return "SCALACS";
+ case Epson: return "Seiko Epson Corporation, Inc.";
+ case Sierra: return "Sierra Semiconductor Corporation";
+ case Siliconsoft: return "Silicon Software, Inc.";
+ case Sonicfoundry: return "Sonic Foundry";
+ case Speechcomp: return "Speech Compression";
+ case Supermac: return "Supermac Technology, Inc.";
+ case Tandy: return "Tandy Corporation";
+ case Korg: return "Toshihiko Okuhura, Korg, Inc.";
+ case Truevision: return "Truevision, Inc.";
+ case TurtleBeach: return "Turtle Beach Systems";
+ case Val: return "Video Associates Labs, Inc.";
+ case Videologic: return "VideoLogic, Inc.";
+ case Vitec: return "Visual Information Technologies, Inc.";
+ case Vocaltec: return "VocalTec, Inc.";
+ case Voyetra: return "Voyetra Technologies";
+ case Wanglabs: return "Wang Laboratories";
+ case Willowpond: return "Willow Pond Corporation";
+ case Winnov: return "Winnov, LP";
+ case Xebec: return "Xebec Multimedia Solutions Limited";
+ case Yamaha: return "Yamaha Corporation of America";
+ default: return String.Format("Unknown Manufacturer ({0})",manufacturerId);
+ }
+ }
+ **/
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Wave/MmeInterop/MmException.cs b/NAudio/Wave/MmeInterop/MmException.cs
new file mode 100644
index 00000000..a7ef21c0
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/MmException.cs
@@ -0,0 +1,53 @@
+using System;
+
+namespace NAudio
+{
+ ///
+ /// Summary description for MmException.
+ ///
+ public class MmException : Exception
+ {
+ private MmResult result;
+ private string function;
+
+ ///
+ /// Creates a new MmException
+ ///
+ /// The result returned by the Windows API call
+ /// The name of the Windows API that failed
+ public MmException(MmResult result, string function)
+ : base(MmException.ErrorMessage(result, function))
+ {
+ this.result = result;
+ this.function = function;
+ }
+
+
+ private static string ErrorMessage(MmResult result, string function)
+ {
+ return String.Format("{0} calling {1}", result, function);
+ }
+
+ ///
+ /// Helper function to automatically raise an exception on failure
+ ///
+ /// The result of the API call
+ /// The API function name
+ public static void Try(MmResult result, string function)
+ {
+ if (result != MmResult.NoError)
+ throw new MmException(result, function);
+ }
+
+ ///
+ /// Returns the Windows API result
+ ///
+ public MmResult Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/MmResult.cs b/NAudio/Wave/MmeInterop/MmResult.cs
new file mode 100644
index 00000000..400e6890
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/MmResult.cs
@@ -0,0 +1,85 @@
+using System;
+
+namespace NAudio
+{
+ ///
+ /// Windows multimedia error codes from mmsystem.h.
+ ///
+ public enum MmResult
+ {
+ /// no error, MMSYSERR_NOERROR
+ NoError = 0,
+ /// unspecified error, MMSYSERR_ERROR
+ UnspecifiedError = 1,
+ /// device ID out of range, MMSYSERR_BADDEVICEID
+ BadDeviceId = 2,
+ /// driver failed enable, MMSYSERR_NOTENABLED
+ NotEnabled = 3,
+ /// device already allocated, MMSYSERR_ALLOCATED
+ AlreadyAllocated = 4,
+ /// device handle is invalid, MMSYSERR_INVALHANDLE
+ InvalidHandle = 5,
+ /// no device driver present, MMSYSERR_NODRIVER
+ NoDriver = 6,
+ /// memory allocation error, MMSYSERR_NOMEM
+ MemoryAllocationError = 7,
+ /// function isn't supported, MMSYSERR_NOTSUPPORTED
+ NotSupported = 8,
+ /// error value out of range, MMSYSERR_BADERRNUM
+ BadErrorNumber = 9,
+ /// invalid flag passed, MMSYSERR_INVALFLAG
+ InvalidFlag = 10,
+ /// invalid parameter passed, MMSYSERR_INVALPARAM
+ InvalidParameter = 11,
+ /// handle being used simultaneously on another thread (eg callback),MMSYSERR_HANDLEBUSY
+ HandleBusy = 12,
+ /// specified alias not found, MMSYSERR_INVALIDALIAS
+ InvalidAlias = 13,
+ /// bad registry database, MMSYSERR_BADDB
+ BadRegistryDatabase = 14,
+ /// registry key not found, MMSYSERR_KEYNOTFOUND
+ RegistryKeyNotFound = 15,
+ /// registry read error, MMSYSERR_READERROR
+ RegistryReadError = 16,
+ /// registry write error, MMSYSERR_WRITEERROR
+ RegistryWriteError = 17,
+ /// registry delete error, MMSYSERR_DELETEERROR
+ RegistryDeleteError = 18,
+ /// registry value not found, MMSYSERR_VALNOTFOUND
+ RegistryValueNotFound = 19,
+ /// driver does not call DriverCallback, MMSYSERR_NODRIVERCB
+ NoDriverCallback = 20,
+ /// more data to be returned, MMSYSERR_MOREDATA
+ MoreData = 21,
+
+ /// unsupported wave format, WAVERR_BADFORMAT
+ WaveBadFormat = 32,
+ /// still something playing, WAVERR_STILLPLAYING
+ WaveStillPlaying = 33,
+ /// header not prepared, WAVERR_UNPREPARED
+ WaveHeaderUnprepared = 34,
+ /// device is synchronous, WAVERR_SYNC
+ WaveSync = 35,
+
+ // ACM error codes, found in msacm.h
+
+ /// Conversion not possible (ACMERR_NOTPOSSIBLE)
+ AcmNotPossible = 512,
+ /// Busy (ACMERR_BUSY)
+ AcmBusy = 513,
+ /// Header Unprepared (ACMERR_UNPREPARED)
+ AcmHeaderUnprepared = 514,
+ /// Cancelled (ACMERR_CANCELED)
+ AcmCancelled = 515,
+
+ // Mixer error codes, found in mmresult.h
+
+ /// invalid line (MIXERR_INVALLINE)
+ MixerInvalidLine = 1024,
+ /// invalid control (MIXERR_INVALCONTROL)
+ MixerInvalidControl = 1025,
+ /// invalid value (MIXERR_INVALVALUE)
+ MixerInvalidValue = 1026,
+ }
+
+}
diff --git a/NAudio/Wave/MmeInterop/MmTime.cs b/NAudio/Wave/MmeInterop/MmTime.cs
new file mode 100644
index 00000000..c22959f4
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/MmTime.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ // http://msdn.microsoft.com/en-us/library/dd757347(v=VS.85).aspx
+ [StructLayout(LayoutKind.Explicit)]
+ struct MmTime
+ {
+ public const int TIME_MS = 0x0001;
+ public const int TIME_SAMPLES = 0x0002;
+ public const int TIME_BYTES = 0x0004;
+
+ [FieldOffset(0)]
+ public UInt32 wType;
+ [FieldOffset(4)]
+ public UInt32 ms;
+ [FieldOffset(4)]
+ public UInt32 sample;
+ [FieldOffset(4)]
+ public UInt32 cb;
+ [FieldOffset(4)]
+ public UInt32 ticks;
+ [FieldOffset(4)]
+ public Byte smpteHour;
+ [FieldOffset(5)]
+ public Byte smpteMin;
+ [FieldOffset(6)]
+ public Byte smpteSec;
+ [FieldOffset(7)]
+ public Byte smpteFrame;
+ [FieldOffset(8)]
+ public Byte smpteFps;
+ [FieldOffset(9)]
+ public Byte smpteDummy;
+ [FieldOffset(10)]
+ public Byte smptePad0;
+ [FieldOffset(11)]
+ public Byte smptePad1;
+ [FieldOffset(4)]
+ public UInt32 midiSongPtrPos;
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveCallbackInfo.cs b/NAudio/Wave/MmeInterop/WaveCallbackInfo.cs
new file mode 100644
index 00000000..fb713e7b
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveCallbackInfo.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Wave Callback Info
+ ///
+ public class WaveCallbackInfo
+ {
+ ///
+ /// Callback Strategy
+ ///
+ public WaveCallbackStrategy Strategy { get; private set; }
+ ///
+ /// Window Handle (if applicable)
+ ///
+ public IntPtr Handle { get; private set; }
+
+ private WaveWindow waveOutWindow;
+ private WaveWindowNative waveOutWindowNative;
+
+ ///
+ /// Sets up a new WaveCallbackInfo for function callbacks
+ ///
+ public static WaveCallbackInfo FunctionCallback()
+ {
+ return new WaveCallbackInfo(WaveCallbackStrategy.FunctionCallback, IntPtr.Zero);
+ }
+
+ ///
+ /// Sets up a new WaveCallbackInfo to use a New Window
+ /// IMPORTANT: only use this on the GUI thread
+ ///
+ public static WaveCallbackInfo NewWindow()
+ {
+ return new WaveCallbackInfo(WaveCallbackStrategy.NewWindow, IntPtr.Zero);
+ }
+
+ ///
+ /// Sets up a new WaveCallbackInfo to use an existing window
+ /// IMPORTANT: only use this on the GUI thread
+ ///
+ public static WaveCallbackInfo ExistingWindow(IntPtr handle)
+ {
+ if (handle == IntPtr.Zero)
+ {
+ throw new ArgumentException("Handle cannot be zero");
+ }
+ return new WaveCallbackInfo(WaveCallbackStrategy.ExistingWindow, handle);
+ }
+
+ private WaveCallbackInfo(WaveCallbackStrategy strategy, IntPtr handle)
+ {
+ this.Strategy = strategy;
+ this.Handle = handle;
+ }
+
+ internal void Connect(WaveInterop.WaveCallback callback)
+ {
+ if (Strategy == WaveCallbackStrategy.NewWindow)
+ {
+ waveOutWindow = new WaveWindow(callback);
+ waveOutWindow.CreateControl();
+ this.Handle = waveOutWindow.Handle;
+ }
+ else if (Strategy == WaveCallbackStrategy.ExistingWindow)
+ {
+ waveOutWindowNative = new WaveWindowNative(callback);
+ waveOutWindowNative.AssignHandle(this.Handle);
+ }
+ }
+
+ internal MmResult WaveOutOpen(out IntPtr waveOutHandle, int deviceNumber, WaveFormat waveFormat, WaveInterop.WaveCallback callback)
+ {
+ MmResult result;
+ if (Strategy == WaveCallbackStrategy.FunctionCallback)
+ {
+ result = WaveInterop.waveOutOpen(out waveOutHandle, (IntPtr)deviceNumber, waveFormat, callback, IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackFunction);
+ }
+ else
+ {
+ result = WaveInterop.waveOutOpenWindow(out waveOutHandle, (IntPtr)deviceNumber, waveFormat, this.Handle, IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackWindow);
+ }
+ return result;
+ }
+
+ internal MmResult WaveInOpen(out IntPtr waveInHandle, int deviceNumber, WaveFormat waveFormat, WaveInterop.WaveCallback callback)
+ {
+ MmResult result;
+ if (Strategy == WaveCallbackStrategy.FunctionCallback)
+ {
+ result = WaveInterop.waveInOpen(out waveInHandle, (IntPtr)deviceNumber, waveFormat, callback, IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackFunction);
+ }
+ else
+ {
+ result = WaveInterop.waveInOpenWindow(out waveInHandle, (IntPtr)deviceNumber, waveFormat, this.Handle, IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackWindow);
+ }
+ return result;
+ }
+
+ internal void Disconnect()
+ {
+ if (waveOutWindow != null)
+ {
+ waveOutWindow.Close();
+ waveOutWindow = null;
+ }
+ if (waveOutWindowNative != null)
+ {
+ waveOutWindowNative.ReleaseHandle();
+ waveOutWindowNative = null;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveCallbackStrategy.cs b/NAudio/Wave/MmeInterop/WaveCallbackStrategy.cs
new file mode 100644
index 00000000..0a2b3a83
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveCallbackStrategy.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Wave Callback Strategy
+ ///
+ public enum WaveCallbackStrategy
+ {
+ ///
+ /// Use a function
+ ///
+ FunctionCallback,
+ ///
+ /// Create a new window (should only be done if on GUI thread)
+ ///
+ NewWindow,
+ ///
+ /// Use an existing window handle
+ ///
+ ExistingWindow,
+ ///
+ /// Use an event handle
+ ///
+ Event,
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveHeader.cs b/NAudio/Wave/MmeInterop/WaveHeader.cs
new file mode 100644
index 00000000..e705c6b3
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveHeader.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveHeader interop structure (WAVEHDR)
+ /// http://msdn.microsoft.com/en-us/library/dd743837%28VS.85%29.aspx
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ class WaveHeader
+ {
+ /// pointer to locked data buffer (lpData)
+ public IntPtr dataBuffer;
+ /// length of data buffer (dwBufferLength)
+ public int bufferLength;
+ /// used for input only (dwBytesRecorded)
+ public int bytesRecorded;
+ /// for client's use (dwUser)
+ public IntPtr userData;
+ /// assorted flags (dwFlags)
+ public WaveHeaderFlags flags;
+ /// loop control counter (dwLoops)
+ public int loops;
+ /// PWaveHdr, reserved for driver (lpNext)
+ public IntPtr next;
+ /// reserved for driver
+ public IntPtr reserved;
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveHeaderFlags.cs b/NAudio/Wave/MmeInterop/WaveHeaderFlags.cs
new file mode 100644
index 00000000..668fe63c
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveHeaderFlags.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Wave Header Flags enumeration
+ ///
+ [Flags]
+ public enum WaveHeaderFlags
+ {
+ ///
+ /// WHDR_BEGINLOOP
+ /// This buffer is the first buffer in a loop. This flag is used only with output buffers.
+ ///
+ BeginLoop = 0x00000004,
+ ///
+ /// WHDR_DONE
+ /// Set by the device driver to indicate that it is finished with the buffer and is returning it to the application.
+ ///
+ Done = 0x00000001,
+ ///
+ /// WHDR_ENDLOOP
+ /// This buffer is the last buffer in a loop. This flag is used only with output buffers.
+ ///
+ EndLoop = 0x00000008,
+ ///
+ /// WHDR_INQUEUE
+ /// Set by Windows to indicate that the buffer is queued for playback.
+ ///
+ InQueue = 0x00000010,
+ ///
+ /// WHDR_PREPARED
+ /// Set by Windows to indicate that the buffer has been prepared with the waveInPrepareHeader or waveOutPrepareHeader function.
+ ///
+ Prepared = 0x00000002
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveInCapabilities.cs b/NAudio/Wave/MmeInterop/WaveInCapabilities.cs
new file mode 100644
index 00000000..7312c32a
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveInCapabilities.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Runtime.InteropServices;
+using Microsoft.Win32;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveInCapabilities structure (based on WAVEINCAPS2 from mmsystem.h)
+ /// http://msdn.microsoft.com/en-us/library/ms713726(VS.85).aspx
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public struct WaveInCapabilities
+ {
+ ///
+ /// wMid
+ ///
+ private short manufacturerId;
+ ///
+ /// wPid
+ ///
+ private short productId;
+ ///
+ /// vDriverVersion
+ ///
+ private int driverVersion;
+ ///
+ /// Product Name (szPname)
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxProductNameLength)]
+ private string productName;
+ ///
+ /// Supported formats (bit flags) dwFormats
+ ///
+ private SupportedWaveFormat supportedFormats;
+ ///
+ /// Supported channels (1 for mono 2 for stereo) (wChannels)
+ /// Seems to be set to -1 on a lot of devices
+ ///
+ private short channels;
+ ///
+ /// wReserved1
+ ///
+ private short reserved;
+
+ // extra WAVEINCAPS2 members
+ private Guid manufacturerGuid;
+ private Guid productGuid;
+ private Guid nameGuid;
+
+ private const int MaxProductNameLength = 32;
+
+ ///
+ /// Number of channels supported
+ ///
+ public int Channels
+ {
+ get
+ {
+ return channels;
+ }
+ }
+
+ ///
+ /// The product name
+ ///
+ public string ProductName
+ {
+ get
+ {
+ return productName;
+ }
+ }
+
+ ///
+ /// The device name Guid (if provided)
+ ///
+ public Guid NameGuid { get { return nameGuid; } }
+ ///
+ /// The product name Guid (if provided)
+ ///
+ public Guid ProductGuid { get { return productGuid; } }
+ ///
+ /// The manufacturer guid (if provided)
+ ///
+ public Guid ManufacturerGuid { get { return manufacturerGuid; } }
+
+ ///
+ /// Checks to see if a given SupportedWaveFormat is supported
+ ///
+ /// The SupportedWaveFormat
+ /// true if supported
+ public bool SupportsWaveFormat(SupportedWaveFormat waveFormat)
+ {
+ return (supportedFormats & waveFormat) == waveFormat;
+ }
+
+ }
+
+ internal static class WaveCapabilitiesHelpers
+ {
+ public static readonly Guid MicrosoftDefaultManufacturerId = new Guid("d5a47fa8-6d98-11d1-a21a-00a0c9223196");
+ public static readonly Guid DefaultWaveOutGuid = new Guid("E36DC310-6D9A-11D1-A21A-00A0C9223196");
+ public static readonly Guid DefaultWaveInGuid = new Guid("E36DC311-6D9A-11D1-A21A-00A0C9223196");
+
+ ///
+ /// The device name from the registry if supported
+ ///
+ public static string GetNameFromGuid(Guid guid)
+ {
+ // n.b it seems many audio drivers just return the default values, which won't be in the registry
+ // http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2006-08/msg00102.html
+ string name = null;
+ using (var namesKey = Registry.LocalMachine.OpenSubKey(@"System\CurrentControlSet\Control\MediaCategories"))
+ using (var nameKey = namesKey.OpenSubKey(guid.ToString("B")))
+ {
+ if (nameKey != null) name = nameKey.GetValue("Name") as string;
+ }
+ return name;
+ }
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveInEventArgs.cs b/NAudio/Wave/MmeInterop/WaveInEventArgs.cs
new file mode 100644
index 00000000..a143992e
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveInEventArgs.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Event Args for WaveInStream event
+ ///
+ public class WaveInEventArgs : EventArgs
+ {
+ private byte[] buffer;
+ private int bytes;
+
+ ///
+ /// Creates new WaveInEventArgs
+ ///
+ public WaveInEventArgs(byte[] buffer, int bytes)
+ {
+ this.buffer = buffer;
+ this.bytes = bytes;
+ }
+
+ ///
+ /// Buffer containing recorded data. Note that it might not be completely
+ /// full.
+ ///
+ public byte[] Buffer
+ {
+ get { return buffer; }
+ }
+
+ ///
+ /// The number of recorded bytes in Buffer.
+ ///
+ public int BytesRecorded
+ {
+ get { return bytes; }
+ }
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveInterop.cs b/NAudio/Wave/MmeInterop/WaveInterop.cs
new file mode 100644
index 00000000..9c6bbd20
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveInterop.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// MME Wave function interop
+ ///
+ class WaveInterop
+ {
+ [Flags]
+ public enum WaveInOutOpenFlags
+ {
+ ///
+ /// CALLBACK_NULL
+ /// No callback
+ ///
+ CallbackNull = 0,
+ ///
+ /// CALLBACK_FUNCTION
+ /// dwCallback is a FARPROC
+ ///
+ CallbackFunction = 0x30000,
+ ///
+ /// CALLBACK_EVENT
+ /// dwCallback is an EVENT handle
+ ///
+ CallbackEvent = 0x50000,
+ ///
+ /// CALLBACK_WINDOW
+ /// dwCallback is a HWND
+ ///
+ CallbackWindow = 0x10000,
+ ///
+ /// CALLBACK_THREAD
+ /// callback is a thread ID
+ ///
+ CallbackThread = 0x20000,
+ /*
+ WAVE_FORMAT_QUERY = 1,
+ WAVE_MAPPED = 4,
+ WAVE_FORMAT_DIRECT = 8*/
+ }
+
+ //public const int TIME_MS = 0x0001; // time in milliseconds
+ //public const int TIME_SAMPLES = 0x0002; // number of wave samples
+ //public const int TIME_BYTES = 0x0004; // current byte offset
+
+ public enum WaveMessage
+ {
+ ///
+ /// WIM_OPEN
+ ///
+ WaveInOpen = 0x3BE,
+ ///
+ /// WIM_CLOSE
+ ///
+ WaveInClose = 0x3BF,
+ ///
+ /// WIM_DATA
+ ///
+ WaveInData = 0x3C0,
+
+ ///
+ /// WOM_CLOSE
+ ///
+ WaveOutClose = 0x3BC,
+ ///
+ /// WOM_DONE
+ ///
+ WaveOutDone = 0x3BD,
+ ///
+ /// WOM_OPEN
+ ///
+ WaveOutOpen = 0x3BB
+ }
+
+ // use the userdata as a reference
+ // WaveOutProc http://msdn.microsoft.com/en-us/library/dd743869%28VS.85%29.aspx
+ // WaveInProc http://msdn.microsoft.com/en-us/library/dd743849%28VS.85%29.aspx
+ public delegate void WaveCallback(IntPtr hWaveOut, WaveMessage message, IntPtr dwInstance, WaveHeader wavhdr, IntPtr dwReserved);
+
+ [DllImport("winmm.dll")]
+ public static extern Int32 mmioStringToFOURCC([MarshalAs(UnmanagedType.LPStr)] String s, int flags);
+
+ [DllImport("winmm.dll")]
+ public static extern Int32 waveOutGetNumDevs();
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutPrepareHeader(IntPtr hWaveOut, WaveHeader lpWaveOutHdr, int uSize);
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutUnprepareHeader(IntPtr hWaveOut, WaveHeader lpWaveOutHdr, int uSize);
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutWrite(IntPtr hWaveOut, WaveHeader lpWaveOutHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd743866%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutOpen(out IntPtr hWaveOut, IntPtr uDeviceID, WaveFormat lpFormat, WaveCallback dwCallback, IntPtr dwInstance, WaveInOutOpenFlags dwFlags);
+ [DllImport("winmm.dll", EntryPoint = "waveOutOpen")]
+ public static extern MmResult waveOutOpenWindow(out IntPtr hWaveOut, IntPtr uDeviceID, WaveFormat lpFormat, IntPtr callbackWindowHandle, IntPtr dwInstance, WaveInOutOpenFlags dwFlags);
+
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutReset(IntPtr hWaveOut);
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutClose(IntPtr hWaveOut);
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutPause(IntPtr hWaveOut);
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutRestart(IntPtr hWaveOut);
+
+ // http://msdn.microsoft.com/en-us/library/dd743863%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutGetPosition(IntPtr hWaveOut, out MmTime mmTime, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd743874%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutSetVolume(IntPtr hWaveOut, int dwVolume);
+
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveOutGetVolume(IntPtr hWaveOut, out int dwVolume);
+
+ // http://msdn.microsoft.com/en-us/library/dd743857%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Auto)]
+ public static extern MmResult waveOutGetDevCaps(IntPtr deviceID, out WaveOutCapabilities waveOutCaps, int waveOutCapsSize);
+
+ [DllImport("winmm.dll")]
+ public static extern Int32 waveInGetNumDevs();
+
+ // http://msdn.microsoft.com/en-us/library/dd743841%28VS.85%29.aspx
+ [DllImport("winmm.dll", CharSet = CharSet.Auto)]
+ public static extern MmResult waveInGetDevCaps(IntPtr deviceID, out WaveInCapabilities waveInCaps, int waveInCapsSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd743838%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInAddBuffer(IntPtr hWaveIn, WaveHeader pwh, int cbwh);
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInClose(IntPtr hWaveIn);
+
+ // http://msdn.microsoft.com/en-us/library/dd743847%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInOpen(out IntPtr hWaveIn, IntPtr uDeviceID, WaveFormat lpFormat, WaveCallback dwCallback, IntPtr dwInstance, WaveInOutOpenFlags dwFlags);
+ [DllImport("winmm.dll", EntryPoint = "waveInOpen")]
+ public static extern MmResult waveInOpenWindow(out IntPtr hWaveIn, IntPtr uDeviceID, WaveFormat lpFormat, IntPtr callbackWindowHandle, IntPtr dwInstance, WaveInOutOpenFlags dwFlags);
+
+ // http://msdn.microsoft.com/en-us/library/dd743848%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInPrepareHeader(IntPtr hWaveIn, WaveHeader lpWaveInHdr, int uSize);
+
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInUnprepareHeader(IntPtr hWaveIn, WaveHeader lpWaveInHdr, int uSize);
+
+ // http://msdn.microsoft.com/en-us/library/dd743850%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInReset(IntPtr hWaveIn);
+
+ // http://msdn.microsoft.com/en-us/library/dd743851%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInStart(IntPtr hWaveIn);
+
+ // http://msdn.microsoft.com/en-us/library/dd743852%28VS.85%29.aspx
+ [DllImport("winmm.dll")]
+ public static extern MmResult waveInStop(IntPtr hWaveIn);
+
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveOutCapabilities.cs b/NAudio/Wave/MmeInterop/WaveOutCapabilities.cs
new file mode 100644
index 00000000..70b2f30c
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveOutCapabilities.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveOutCapabilities structure (based on WAVEOUTCAPS2 from mmsystem.h)
+ /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/multimed/htm/_win32_waveoutcaps_str.asp
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+ public struct WaveOutCapabilities
+ {
+ ///
+ /// wMid
+ ///
+ private short manufacturerId;
+ ///
+ /// wPid
+ ///
+ private short productId;
+ ///
+ /// vDriverVersion
+ ///
+ private int driverVersion;
+ ///
+ /// Product Name (szPname)
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MaxProductNameLength)]
+ private string productName;
+ ///
+ /// Supported formats (bit flags) dwFormats
+ ///
+ private SupportedWaveFormat supportedFormats;
+ ///
+ /// Supported channels (1 for mono 2 for stereo) (wChannels)
+ /// Seems to be set to -1 on a lot of devices
+ ///
+ private short channels;
+ ///
+ /// wReserved1
+ ///
+ private short reserved;
+ ///
+ /// Optional functionality supported by the device
+ ///
+ private WaveOutSupport support; // = new WaveOutSupport();
+
+ // extra WAVEOUTCAPS2 members
+ private Guid manufacturerGuid;
+ private Guid productGuid;
+ private Guid nameGuid;
+
+ private const int MaxProductNameLength = 32;
+
+ ///
+ /// Number of channels supported
+ ///
+ public int Channels
+ {
+ get
+ {
+ return channels;
+ }
+ }
+
+ ///
+ /// Whether playback control is supported
+ ///
+ public bool SupportsPlaybackRateControl
+ {
+ get
+ {
+ return (support & WaveOutSupport.PlaybackRate) == WaveOutSupport.PlaybackRate;
+ }
+ }
+
+ ///
+ /// The product name
+ ///
+ public string ProductName
+ {
+ get
+ {
+ return productName;
+ }
+ }
+
+ ///
+ /// Checks to see if a given SupportedWaveFormat is supported
+ ///
+ /// The SupportedWaveFormat
+ /// true if supported
+ public bool SupportsWaveFormat(SupportedWaveFormat waveFormat)
+ {
+ return (supportedFormats & waveFormat) == waveFormat;
+ }
+
+ ///
+ /// The device name Guid (if provided)
+ ///
+ public Guid NameGuid { get { return nameGuid; } }
+ ///
+ /// The product name Guid (if provided)
+ ///
+ public Guid ProductGuid { get { return productGuid; } }
+ ///
+ /// The manufacturer guid (if provided)
+ ///
+ public Guid ManufacturerGuid { get { return manufacturerGuid; } }
+ }
+
+ ///
+ /// Supported wave formats for WaveOutCapabilities
+ ///
+ [Flags]
+ public enum SupportedWaveFormat
+ {
+ ///
+ /// 11.025 kHz, Mono, 8-bit
+ ///
+ WAVE_FORMAT_1M08 = 0x00000001,
+ ///
+ /// 11.025 kHz, Stereo, 8-bit
+ ///
+ WAVE_FORMAT_1S08 = 0x00000002,
+ ///
+ /// 11.025 kHz, Mono, 16-bit
+ ///
+ WAVE_FORMAT_1M16 = 0x00000004,
+ ///
+ /// 11.025 kHz, Stereo, 16-bit
+ ///
+ WAVE_FORMAT_1S16 = 0x00000008,
+ ///
+ /// 22.05 kHz, Mono, 8-bit
+ ///
+ WAVE_FORMAT_2M08 = 0x00000010,
+ ///
+ /// 22.05 kHz, Stereo, 8-bit
+ ///
+ WAVE_FORMAT_2S08 = 0x00000020,
+ ///
+ /// 22.05 kHz, Mono, 16-bit
+ ///
+ WAVE_FORMAT_2M16 = 0x00000040,
+ ///
+ /// 22.05 kHz, Stereo, 16-bit
+ ///
+ WAVE_FORMAT_2S16 = 0x00000080,
+ ///
+ /// 44.1 kHz, Mono, 8-bit
+ ///
+ WAVE_FORMAT_4M08 = 0x00000100,
+ ///
+ /// 44.1 kHz, Stereo, 8-bit
+ ///
+ WAVE_FORMAT_4S08 = 0x00000200,
+ ///
+ /// 44.1 kHz, Mono, 16-bit
+ ///
+ WAVE_FORMAT_4M16 = 0x00000400,
+ ///
+ /// 44.1 kHz, Stereo, 16-bit
+ ///
+ WAVE_FORMAT_4S16 = 0x00000800,
+
+ ///
+ /// 44.1 kHz, Mono, 8-bit
+ ///
+ WAVE_FORMAT_44M08 = 0x00000100,
+ ///
+ /// 44.1 kHz, Stereo, 8-bit
+ ///
+ WAVE_FORMAT_44S08 = 0x00000200,
+ ///
+ /// 44.1 kHz, Mono, 16-bit
+ ///
+ WAVE_FORMAT_44M16 = 0x00000400,
+ ///
+ /// 44.1 kHz, Stereo, 16-bit
+ ///
+ WAVE_FORMAT_44S16 = 0x00000800,
+ ///
+ /// 48 kHz, Mono, 8-bit
+ ///
+ WAVE_FORMAT_48M08 = 0x00001000,
+ ///
+ /// 48 kHz, Stereo, 8-bit
+ ///
+ WAVE_FORMAT_48S08 = 0x00002000,
+ ///
+ /// 48 kHz, Mono, 16-bit
+ ///
+ WAVE_FORMAT_48M16 = 0x00004000,
+ ///
+ /// 48 kHz, Stereo, 16-bit
+ ///
+ WAVE_FORMAT_48S16 = 0x00008000,
+ ///
+ /// 96 kHz, Mono, 8-bit
+ ///
+ WAVE_FORMAT_96M08 = 0x00010000,
+ ///
+ /// 96 kHz, Stereo, 8-bit
+ ///
+ WAVE_FORMAT_96S08 = 0x00020000,
+ ///
+ /// 96 kHz, Mono, 16-bit
+ ///
+ WAVE_FORMAT_96M16 = 0x00040000,
+ ///
+ /// 96 kHz, Stereo, 16-bit
+ ///
+ WAVE_FORMAT_96S16 = 0x00080000,
+
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveOutSupport.cs b/NAudio/Wave/MmeInterop/WaveOutSupport.cs
new file mode 100644
index 00000000..c5544e0d
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveOutSupport.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Flags indicating what features this WaveOut device supports
+ ///
+ [Flags]
+ enum WaveOutSupport
+ {
+ /// supports pitch control (WAVECAPS_PITCH)
+ Pitch = 0x0001,
+ /// supports playback rate control (WAVECAPS_PLAYBACKRATE)
+ PlaybackRate = 0x0002,
+ /// supports volume control (WAVECAPS_VOLUME)
+ Volume = 0x0004,
+ /// supports separate left-right volume control (WAVECAPS_LRVOLUME)
+ LRVolume = 0x0008,
+ /// (WAVECAPS_SYNC)
+ Sync = 0x0010,
+ /// (WAVECAPS_SAMPLEACCURATE)
+ SampleAccurate = 0x0020,
+ }
+}
diff --git a/NAudio/Wave/MmeInterop/WaveWindow.cs b/NAudio/Wave/MmeInterop/WaveWindow.cs
new file mode 100644
index 00000000..76669492
--- /dev/null
+++ b/NAudio/Wave/MmeInterop/WaveWindow.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ class WaveWindowNative : System.Windows.Forms.NativeWindow
+ {
+ private WaveInterop.WaveCallback waveCallback;
+
+ public WaveWindowNative(WaveInterop.WaveCallback waveCallback)
+ {
+ this.waveCallback = waveCallback;
+ }
+
+ protected override void WndProc(ref System.Windows.Forms.Message m)
+ {
+ WaveInterop.WaveMessage message = (WaveInterop.WaveMessage)m.Msg;
+
+ switch(message)
+ {
+ case WaveInterop.WaveMessage.WaveOutDone:
+ case WaveInterop.WaveMessage.WaveInData:
+ IntPtr hOutputDevice = m.WParam;
+ WaveHeader waveHeader = new WaveHeader();
+ Marshal.PtrToStructure(m.LParam, waveHeader);
+ waveCallback(hOutputDevice, message, IntPtr.Zero, waveHeader, IntPtr.Zero);
+ break;
+ case WaveInterop.WaveMessage.WaveOutOpen:
+ case WaveInterop.WaveMessage.WaveOutClose:
+ case WaveInterop.WaveMessage.WaveInClose:
+ case WaveInterop.WaveMessage.WaveInOpen:
+ waveCallback(m.WParam, message, IntPtr.Zero, null, IntPtr.Zero);
+ break;
+ default:
+ base.WndProc(ref m);
+ break;
+ }
+ }
+ }
+
+ class WaveWindow : Form
+ {
+ private WaveInterop.WaveCallback waveCallback;
+
+ public WaveWindow(WaveInterop.WaveCallback waveCallback)
+ {
+ this.waveCallback = waveCallback;
+ }
+
+ protected override void WndProc(ref System.Windows.Forms.Message m)
+ {
+ WaveInterop.WaveMessage message = (WaveInterop.WaveMessage)m.Msg;
+
+ switch(message)
+ {
+ case WaveInterop.WaveMessage.WaveOutDone:
+ case WaveInterop.WaveMessage.WaveInData:
+ IntPtr hOutputDevice = m.WParam;
+ WaveHeader waveHeader = new WaveHeader();
+ Marshal.PtrToStructure(m.LParam, waveHeader);
+ waveCallback(hOutputDevice, message, IntPtr.Zero, waveHeader, IntPtr.Zero);
+ break;
+ case WaveInterop.WaveMessage.WaveOutOpen:
+ case WaveInterop.WaveMessage.WaveOutClose:
+ case WaveInterop.WaveMessage.WaveInClose:
+ case WaveInterop.WaveMessage.WaveInOpen:
+ waveCallback(m.WParam, message, IntPtr.Zero, null, IntPtr.Zero);
+ break;
+ default:
+ base.WndProc(ref m);
+ break;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/ISampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/ISampleChunkConverter.cs
new file mode 100644
index 00000000..a8d03140
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/ISampleChunkConverter.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Sample provider interface to make WaveChannel32 extensible
+ /// Still a bit ugly, hence internal at the moment - and might even make these into
+ /// bit depth converting WaveProviders
+ ///
+ interface ISampleChunkConverter
+ {
+ bool Supports(WaveFormat format);
+ void LoadNextChunk(IWaveProvider sourceProvider, int samplePairsRequired);
+ bool GetNextSample(out float sampleLeft, out float sampleRight);
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/Mono16SampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/Mono16SampleChunkConverter.cs
new file mode 100644
index 00000000..6a21bb40
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/Mono16SampleChunkConverter.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class Mono16SampleChunkConverter : ISampleChunkConverter
+ {
+ private int sourceSample;
+ private byte[] sourceBuffer;
+ private WaveBuffer sourceWaveBuffer;
+ private int sourceSamples;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.Pcm &&
+ waveFormat.BitsPerSample == 16 &&
+ waveFormat.Channels == 1;
+ }
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired * 2;
+ sourceSample = 0;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ sourceSamples = source.Read(sourceBuffer, 0, sourceBytesRequired) / 2;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (sourceSample < sourceSamples)
+ {
+ sampleLeft = sourceWaveBuffer.ShortBuffer[sourceSample++] / 32768.0f;
+ sampleRight = sampleLeft;
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/Mono24SampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/Mono24SampleChunkConverter.cs
new file mode 100644
index 00000000..43ad8ba4
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/Mono24SampleChunkConverter.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class Mono24SampleChunkConverter : ISampleChunkConverter
+ {
+ private int offset;
+ private byte[] sourceBuffer;
+ private int sourceBytes;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.Pcm &&
+ waveFormat.BitsPerSample == 24 &&
+ waveFormat.Channels == 1;
+ }
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired * 3;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer,sourceBytesRequired);
+ sourceBytes = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ offset = 0;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (offset < sourceBytes)
+ {
+ sampleLeft = (((sbyte)sourceBuffer[offset + 2] << 16) | (sourceBuffer[offset + 1] << 8) | sourceBuffer[offset]) / 8388608f;
+ offset += 3;
+ sampleRight = sampleLeft;
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/Mono8SampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/Mono8SampleChunkConverter.cs
new file mode 100644
index 00000000..06cb391c
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/Mono8SampleChunkConverter.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class Mono8SampleChunkConverter : ISampleChunkConverter
+ {
+ private int offset;
+ private byte[] sourceBuffer;
+ private int sourceBytes;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.Pcm &&
+ waveFormat.BitsPerSample == 8 &&
+ waveFormat.Channels == 1;
+ }
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ sourceBytes = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ offset = 0;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (offset < sourceBytes)
+ {
+ sampleLeft = sourceBuffer[offset] / 256f;
+ offset++;
+ sampleRight = sampleLeft;
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/MonoFloatSampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/MonoFloatSampleChunkConverter.cs
new file mode 100644
index 00000000..896d3239
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/MonoFloatSampleChunkConverter.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class MonoFloatSampleChunkConverter : ISampleChunkConverter
+ {
+ private int sourceSample;
+ private byte[] sourceBuffer;
+ private WaveBuffer sourceWaveBuffer;
+ private int sourceSamples;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.IeeeFloat &&
+ waveFormat.Channels == 1;
+ }
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired * 4;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ sourceSamples = source.Read(sourceBuffer, 0, sourceBytesRequired) / 4;
+ sourceSample = 0;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (sourceSample < sourceSamples)
+ {
+ sampleLeft = sourceWaveBuffer.FloatBuffer[sourceSample++];
+ sampleRight = sampleLeft;
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/Stereo16SampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/Stereo16SampleChunkConverter.cs
new file mode 100644
index 00000000..fba53816
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/Stereo16SampleChunkConverter.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class Stereo16SampleChunkConverter : ISampleChunkConverter
+ {
+ private int sourceSample;
+ private byte[] sourceBuffer;
+ private WaveBuffer sourceWaveBuffer;
+ private int sourceSamples;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.Pcm &&
+ waveFormat.BitsPerSample == 16 &&
+ waveFormat.Channels == 2;
+ }
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired * 4;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ sourceSamples = source.Read(sourceBuffer, 0, sourceBytesRequired) / 2;
+ sourceSample = 0;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (sourceSample < sourceSamples)
+ {
+ sampleLeft = sourceWaveBuffer.ShortBuffer[sourceSample++] / 32768.0f;
+ sampleRight = sourceWaveBuffer.ShortBuffer[sourceSample++] / 32768.0f;
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/Stereo24SampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/Stereo24SampleChunkConverter.cs
new file mode 100644
index 00000000..b1573f2f
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/Stereo24SampleChunkConverter.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class Stereo24SampleChunkConverter : ISampleChunkConverter
+ {
+ private int offset;
+ private byte[] sourceBuffer;
+ private int sourceBytes;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.Pcm &&
+ waveFormat.BitsPerSample == 24 &&
+ waveFormat.Channels == 2;
+ }
+
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired * 6;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ sourceBytes = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ offset = 0;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (offset < sourceBytes)
+ {
+ sampleLeft = (((sbyte)sourceBuffer[offset + 2] << 16) | (sourceBuffer[offset + 1] << 8) | sourceBuffer[offset]) / 8388608f;
+ offset += 3;
+ sampleRight = (((sbyte)sourceBuffer[offset + 2] << 16) | (sourceBuffer[offset + 1] << 8) | sourceBuffer[offset]) / 8388608f;
+ offset += 3;
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/Stereo8SampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/Stereo8SampleChunkConverter.cs
new file mode 100644
index 00000000..4d702c9c
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/Stereo8SampleChunkConverter.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class Stereo8SampleChunkConverter : ISampleChunkConverter
+ {
+ private int offset;
+ private byte[] sourceBuffer;
+ private int sourceBytes;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.Pcm &&
+ waveFormat.BitsPerSample == 8 &&
+ waveFormat.Channels == 2;
+ }
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired * 2;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ sourceBytes = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ offset = 0;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (offset < sourceBytes)
+ {
+ sampleLeft = sourceBuffer[offset++] / 256f;
+ sampleRight = sourceBuffer[offset++] / 256f;
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleChunkConverters/StereoFloatSampleChunkConverter.cs b/NAudio/Wave/SampleChunkConverters/StereoFloatSampleChunkConverter.cs
new file mode 100644
index 00000000..47e9410b
--- /dev/null
+++ b/NAudio/Wave/SampleChunkConverters/StereoFloatSampleChunkConverter.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ class StereoFloatSampleChunkConverter : ISampleChunkConverter
+ {
+ private int sourceSample;
+ private byte[] sourceBuffer;
+ private WaveBuffer sourceWaveBuffer;
+ private int sourceSamples;
+
+ public bool Supports(WaveFormat waveFormat)
+ {
+ return waveFormat.Encoding == WaveFormatEncoding.IeeeFloat &&
+ waveFormat.Channels == 2;
+ }
+
+ public void LoadNextChunk(IWaveProvider source, int samplePairsRequired)
+ {
+ int sourceBytesRequired = samplePairsRequired * 8;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ sourceSamples = source.Read(sourceBuffer, 0, sourceBytesRequired) / 4;
+ sourceSample = 0;
+ }
+
+ public bool GetNextSample(out float sampleLeft, out float sampleRight)
+ {
+ if (sourceSample < sourceSamples)
+ {
+ sampleLeft = sourceWaveBuffer.FloatBuffer[sourceSample++];
+ sampleRight = sourceWaveBuffer.FloatBuffer[sourceSample++];
+ return true;
+ }
+ else
+ {
+ sampleLeft = 0.0f;
+ sampleRight = 0.0f;
+ return false;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/AdsrSampleProvider.cs b/NAudio/Wave/SampleProviders/AdsrSampleProvider.cs
new file mode 100644
index 00000000..805f6d49
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/AdsrSampleProvider.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Linq;
+using NAudio.Dsp;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// ADSR sample provider allowing you to specify attack, decay, sustain and release values
+ ///
+ public class AdsrSampleProvider : ISampleProvider
+ {
+ private readonly ISampleProvider source;
+ private readonly EnvelopeGenerator adsr;
+ private float attackSeconds;
+ private float releaseSeconds;
+
+ ///
+ /// Creates a new AdsrSampleProvider with default values
+ ///
+ public AdsrSampleProvider(ISampleProvider source)
+ {
+ if (source.WaveFormat.Channels > 1) throw new ArgumentException("Currently only supports mono inputs");
+ this.source = source;
+ adsr = new EnvelopeGenerator();
+ AttackSeconds = 0.01f;
+ adsr.SustainLevel = 1.0f;
+ adsr.DecayRate = 0.0f * WaveFormat.SampleRate;
+ ReleaseSeconds = 0.3f;
+ adsr.Gate(true);
+ }
+
+ ///
+ /// Attack time in seconds
+ ///
+ public float AttackSeconds
+ {
+ get
+ {
+ return attackSeconds;
+ }
+ set
+ {
+ attackSeconds = value;
+ adsr.AttackRate = attackSeconds * WaveFormat.SampleRate;
+ }
+ }
+
+ ///
+ /// Release time in seconds
+ ///
+ public float ReleaseSeconds
+ {
+ get
+ {
+ return releaseSeconds;
+ }
+ set
+ {
+ releaseSeconds = value;
+ adsr.ReleaseRate = releaseSeconds * WaveFormat.SampleRate;
+ }
+ }
+
+ ///
+ /// Reads audio from this sample provider
+ ///
+ public int Read(float[] buffer, int offset, int count)
+ {
+ if (adsr.State == EnvelopeGenerator.EnvelopeState.Idle) return 0; // we've finished
+ var samples = source.Read(buffer, offset, count);
+ for (int n = 0; n < samples; n++)
+ {
+ buffer[offset++] *= adsr.Process();
+ }
+ return samples;
+ }
+
+ ///
+ /// Enters the Release phase
+ ///
+ public void Stop()
+ {
+ adsr.Gate(false);
+ }
+
+ ///
+ /// The output WaveFormat
+ ///
+ public WaveFormat WaveFormat { get { return source.WaveFormat; } }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/FadeInOutSampleProvider.cs b/NAudio/Wave/SampleProviders/FadeInOutSampleProvider.cs
new file mode 100644
index 00000000..569ac9ca
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/FadeInOutSampleProvider.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Sample Provider to allow fading in and out
+ ///
+ public class FadeInOutSampleProvider : ISampleProvider
+ {
+ enum FadeState
+ {
+ Silence,
+ FadingIn,
+ FullVolume,
+ FadingOut,
+ }
+
+ private readonly object lockObject = new object();
+ private readonly ISampleProvider source;
+ private int fadeSamplePosition;
+ private int fadeSampleCount;
+ private FadeState fadeState;
+
+ ///
+ /// Creates a new FadeInOutSampleProvider
+ ///
+ /// The source stream with the audio to be faded in or out
+ /// If true, we start faded out
+ public FadeInOutSampleProvider(ISampleProvider source, bool initiallySilent = false)
+ {
+ this.source = source;
+ this.fadeState = initiallySilent ? FadeState.Silence : FadeState.FullVolume;
+ }
+
+ ///
+ /// Requests that a fade-in begins (will start on the next call to Read)
+ ///
+ /// Duration of fade in milliseconds
+ public void BeginFadeIn(double fadeDurationInMilliseconds)
+ {
+ lock (lockObject)
+ {
+ fadeSamplePosition = 0;
+ fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
+ fadeState = FadeState.FadingIn;
+ }
+ }
+
+ ///
+ /// Requests that a fade-out begins (will start on the next call to Read)
+ ///
+ /// Duration of fade in milliseconds
+ public void BeginFadeOut(double fadeDurationInMilliseconds)
+ {
+ lock (lockObject)
+ {
+ fadeSamplePosition = 0;
+ fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
+ fadeState = FadeState.FadingOut;
+ }
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Buffer to read into
+ /// Offset within buffer to write to
+ /// Number of samples desired
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int sourceSamplesRead = source.Read(buffer, offset, count);
+ lock (lockObject)
+ {
+ if (fadeState == FadeState.FadingIn)
+ {
+ FadeIn(buffer, offset, sourceSamplesRead);
+ }
+ else if (fadeState == FadeState.FadingOut)
+ {
+ FadeOut(buffer, offset, sourceSamplesRead);
+ }
+ else if (fadeState == FadeState.Silence)
+ {
+ ClearBuffer(buffer, offset, count);
+ }
+ }
+ return sourceSamplesRead;
+ }
+
+ private static void ClearBuffer(float[] buffer, int offset, int count)
+ {
+ for (int n = 0; n < count; n++)
+ {
+ buffer[n + offset] = 0;
+ }
+ }
+
+ private void FadeOut(float[] buffer, int offset, int sourceSamplesRead)
+ {
+ int sample = 0;
+ while (sample < sourceSamplesRead)
+ {
+ float multiplier = 1.0f - (fadeSamplePosition / (float)fadeSampleCount);
+ for (int ch = 0; ch < source.WaveFormat.Channels; ch++)
+ {
+ buffer[offset + sample++] *= multiplier;
+ }
+ fadeSamplePosition++;
+ if (fadeSamplePosition > fadeSampleCount)
+ {
+ fadeState = FadeState.Silence;
+ // clear out the end
+ ClearBuffer(buffer, sample + offset, sourceSamplesRead - sample);
+ break;
+ }
+ }
+ }
+
+ private void FadeIn(float[] buffer, int offset, int sourceSamplesRead)
+ {
+ int sample = 0;
+ while (sample < sourceSamplesRead)
+ {
+ float multiplier = (fadeSamplePosition / (float)fadeSampleCount);
+ for (int ch = 0; ch < source.WaveFormat.Channels; ch++)
+ {
+ buffer[offset + sample++] *= multiplier;
+ }
+ fadeSamplePosition++;
+ if (fadeSamplePosition > fadeSampleCount)
+ {
+ fadeState = FadeState.FullVolume;
+ // no need to multiply any more
+ break;
+ }
+ }
+ }
+
+ ///
+ /// WaveFormat of this SampleProvider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return source.WaveFormat; }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/MeteringSampleProvider.cs b/NAudio/Wave/SampleProviders/MeteringSampleProvider.cs
new file mode 100644
index 00000000..2500158b
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/MeteringSampleProvider.cs
@@ -0,0 +1,105 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Simple SampleProvider that passes through audio unchanged and raises
+ /// an event every n samples with the maximum sample value from the period
+ /// for metering purposes
+ ///
+ public class MeteringSampleProvider : ISampleProvider
+ {
+ private readonly ISampleProvider source;
+
+ private readonly float[] maxSamples;
+ private int sampleCount;
+ private readonly int channels;
+ private readonly StreamVolumeEventArgs args;
+
+ ///
+ /// Number of Samples per notification
+ ///
+ public int SamplesPerNotification { get; set; }
+
+ ///
+ /// Raised periodically to inform the user of the max volume
+ ///
+ public event EventHandler StreamVolume;
+
+ ///
+ /// Initialises a new instance of MeteringSampleProvider that raises 10 stream volume
+ /// events per second
+ ///
+ /// Source sample provider
+ public MeteringSampleProvider(ISampleProvider source) :
+ this(source, source.WaveFormat.SampleRate / 10)
+ {
+ }
+
+ ///
+ /// Initialises a new instance of MeteringSampleProvider
+ ///
+ /// source sampler provider
+ /// Number of samples between notifications
+ public MeteringSampleProvider(ISampleProvider source, int samplesPerNotification)
+ {
+ this.source = source;
+ this.channels = source.WaveFormat.Channels;
+ this.maxSamples = new float[channels];
+ this.SamplesPerNotification = samplesPerNotification;
+ this.args = new StreamVolumeEventArgs() { MaxSampleValues = this.maxSamples }; // create objects up front giving GC little to do
+ }
+
+ ///
+ /// The WaveFormat of this sample provider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return source.WaveFormat; }
+ }
+
+ ///
+ /// Reads samples from this Sample Provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples required
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int samplesRead = source.Read(buffer, offset, count);
+ // only bother if there is an event listener
+ if (StreamVolume != null)
+ {
+ for (int index = 0; index < samplesRead; index += channels)
+ {
+ for (int channel = 0; channel < channels; channel++)
+ {
+ float sampleValue = Math.Abs(buffer[offset + index + channel]);
+ maxSamples[channel] = Math.Max(maxSamples[channel], sampleValue);
+ }
+ sampleCount++;
+ if (sampleCount >= SamplesPerNotification)
+ {
+ StreamVolume(this, args);
+ sampleCount = 0;
+ // n.b. we avoid creating new instances of anything here
+ Array.Clear(maxSamples, 0, channels);
+ }
+ }
+ }
+ return samplesRead;
+ }
+ }
+
+ ///
+ /// Event args for aggregated stream volume
+ ///
+ public class StreamVolumeEventArgs : EventArgs
+ {
+ ///
+ /// Max sample values array (one for each channel)
+ ///
+ public float[] MaxSampleValues { get; set; }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/MixingSampleProvider.cs b/NAudio/Wave/SampleProviders/MixingSampleProvider.cs
new file mode 100644
index 00000000..938fa803
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/MixingSampleProvider.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// A sample provider mixer, allowing inputs to be added and removed
+ ///
+ public class MixingSampleProvider : ISampleProvider
+ {
+ private List sources;
+ private WaveFormat waveFormat;
+ private float[] sourceBuffer;
+ private const int maxInputs = 1024; // protect ourselves against doing something silly
+
+ ///
+ /// Creates a new MixingSampleProvider, with no inputs, but a specified WaveFormat
+ ///
+ /// The WaveFormat of this mixer. All inputs must be in this format
+ public MixingSampleProvider(WaveFormat waveFormat)
+ {
+ if (waveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ {
+ throw new ArgumentException("Mixer wave format must be IEEE float");
+ }
+ this.sources = new List();
+ this.waveFormat = waveFormat;
+ }
+
+ ///
+ /// Creates a new MixingSampleProvider, based on the given inputs
+ ///
+ /// Mixer inputs - must all have the same waveformat, and must
+ /// all be of the same WaveFormat. There must be at least one input
+ public MixingSampleProvider(IEnumerable sources)
+ {
+ this.sources = new List();
+ foreach (var source in sources)
+ {
+ AddMixerInput(source);
+ }
+ if (this.sources.Count == 0)
+ {
+ throw new ArgumentException("Must provide at least one input in this constructor");
+ }
+ }
+
+ ///
+ /// When set to true, the Read method always returns the number
+ /// of samples requested, even if there are no inputs, or if the
+ /// current inputs reach their end. Setting this to true effectively
+ /// makes this a never-ending sample provider, so take care if you plan
+ /// to write it out to a file.
+ ///
+ public bool ReadFully { get; set; }
+
+ ///
+ /// Adds a WaveProvider as a Mixer input.
+ /// Must be PCM or IEEE float already
+ ///
+ /// IWaveProvider mixer input
+ public void AddMixerInput(IWaveProvider mixerInput)
+ {
+ AddMixerInput(SampleProviderConverters.ConvertWaveProviderIntoSampleProvider(mixerInput));
+ }
+
+ ///
+ /// Adds a new mixer input
+ ///
+ /// Mixer input
+ public void AddMixerInput(ISampleProvider mixerInput)
+ {
+ // we'll just call the lock around add since we are protecting against an AddMixerInput at
+ // the same time as a Read, rather than two AddMixerInput calls at the same time
+ lock (sources)
+ {
+ if (this.sources.Count >= maxInputs)
+ {
+ throw new InvalidOperationException("Too many mixer inputs");
+ }
+ this.sources.Add(mixerInput);
+ }
+ if (this.waveFormat == null)
+ {
+ this.waveFormat = mixerInput.WaveFormat;
+ }
+ else
+ {
+ if (this.WaveFormat.SampleRate != mixerInput.WaveFormat.SampleRate ||
+ this.WaveFormat.Channels != mixerInput.WaveFormat.Channels)
+ {
+ throw new ArgumentException("All mixer inputs must have the same WaveFormat");
+ }
+ }
+ }
+
+ ///
+ /// Removes a mixer input
+ ///
+ /// Mixer input to remove
+ public void RemoveMixerInput(ISampleProvider mixerInput)
+ {
+ lock (sources)
+ {
+ this.sources.Remove(mixerInput);
+ }
+ }
+
+ ///
+ /// Removes all mixer inputs
+ ///
+ public void RemoveAllMixerInputs()
+ {
+ lock (sources)
+ {
+ this.sources.Clear();
+ }
+ }
+
+ ///
+ /// The output WaveFormat of this sample provider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.waveFormat; }
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples required
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int outputSamples = 0;
+ this.sourceBuffer = BufferHelpers.Ensure(this.sourceBuffer, count);
+ lock (sources)
+ {
+ int index = sources.Count - 1;
+ while (index >= 0)
+ {
+ var source = sources[index];
+ int samplesRead = source.Read(this.sourceBuffer, 0, count);
+ int outIndex = offset;
+ for (int n = 0; n < samplesRead; n++)
+ {
+ if (n >= outputSamples)
+ {
+ buffer[outIndex++] = this.sourceBuffer[n];
+ }
+ else
+ {
+ buffer[outIndex++] += this.sourceBuffer[n];
+ }
+ }
+ outputSamples = Math.Max(samplesRead, outputSamples);
+ if (samplesRead == 0)
+ {
+ sources.RemoveAt(index);
+ }
+ index--;
+ }
+ }
+ // optionally ensure we return a full buffer
+ if (ReadFully && outputSamples < count)
+ {
+ int outputIndex = offset + outputSamples;
+ while (outputIndex < offset + count)
+ {
+ buffer[outputIndex++] = 0;
+ }
+ outputSamples = count;
+ }
+ return outputSamples;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/MonoToStereoSampleProvider.cs b/NAudio/Wave/SampleProviders/MonoToStereoSampleProvider.cs
new file mode 100644
index 00000000..c2647e2a
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/MonoToStereoSampleProvider.cs
@@ -0,0 +1,66 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// No nonsense mono to stereo provider, no volume adjustment,
+ /// just copies input to left and right.
+ ///
+ public class MonoToStereoSampleProvider : ISampleProvider
+ {
+ private readonly ISampleProvider source;
+ private readonly WaveFormat waveFormat;
+ private float[] sourceBuffer;
+
+ ///
+ /// Initializes a new instance of MonoToStereoSampleProvider
+ ///
+ /// Source sample provider
+ public MonoToStereoSampleProvider(ISampleProvider source)
+ {
+ if (source.WaveFormat.Channels != 1)
+ {
+ throw new ArgumentException("Source must be mono");
+ }
+ this.source = source;
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(source.WaveFormat.SampleRate, 2);
+ }
+
+ ///
+ /// WaveFormat of this provider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.waveFormat; }
+ }
+
+ ///
+ /// Reads samples from this provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples required
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int sourceSamplesRequired = count / 2;
+ int outIndex = offset;
+ EnsureSourceBuffer(sourceSamplesRequired);
+ int sourceSamplesRead = source.Read(sourceBuffer, 0, sourceSamplesRequired);
+ for (int n = 0; n < sourceSamplesRead; n++)
+ {
+ buffer[outIndex++] = sourceBuffer[n];
+ buffer[outIndex++] = sourceBuffer[n];
+ }
+ return sourceSamplesRead * 2;
+ }
+
+ private void EnsureSourceBuffer(int count)
+ {
+ if (this.sourceBuffer == null || this.sourceBuffer.Length < count)
+ {
+ this.sourceBuffer = new float[count];
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/MultiplexingSampleProvider.cs b/NAudio/Wave/SampleProviders/MultiplexingSampleProvider.cs
new file mode 100644
index 00000000..0c225c6e
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/MultiplexingSampleProvider.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Allows any number of inputs to be patched to outputs
+ /// Uses could include swapping left and right channels, turning mono into stereo,
+ /// feeding different input sources to different soundcard outputs etc
+ ///
+ public class MultiplexingSampleProvider : ISampleProvider
+ {
+ private readonly IList inputs;
+ private readonly WaveFormat waveFormat;
+ private readonly int outputChannelCount;
+ private readonly int inputChannelCount;
+ private readonly List mappings;
+
+ ///
+ /// Creates a multiplexing sample provider, allowing re-patching of input channels to different
+ /// output channels
+ ///
+ /// Input sample providers. Must all be of the same sample rate, but can have any number of channels
+ /// Desired number of output channels.
+ public MultiplexingSampleProvider(IEnumerable inputs, int numberOfOutputChannels)
+ {
+ this.inputs = new List(inputs);
+ this.outputChannelCount = numberOfOutputChannels;
+
+ if (this.inputs.Count == 0)
+ {
+ throw new ArgumentException("You must provide at least one input");
+ }
+ if (numberOfOutputChannels < 1)
+ {
+ throw new ArgumentException("You must provide at least one output");
+ }
+ foreach (var input in this.inputs)
+ {
+ if (this.waveFormat == null)
+ {
+ if (input.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ {
+ throw new ArgumentException("Only 32 bit float is supported");
+ }
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(input.WaveFormat.SampleRate, numberOfOutputChannels);
+ }
+ else
+ {
+ if (input.WaveFormat.BitsPerSample != this.waveFormat.BitsPerSample)
+ {
+ throw new ArgumentException("All inputs must have the same bit depth");
+ }
+ if (input.WaveFormat.SampleRate != this.waveFormat.SampleRate)
+ {
+ throw new ArgumentException("All inputs must have the same sample rate");
+ }
+ }
+ inputChannelCount += input.WaveFormat.Channels;
+ }
+
+ mappings = new List();
+ for (int n = 0; n < outputChannelCount; n++)
+ {
+ mappings.Add(n % inputChannelCount);
+ }
+ }
+
+ ///
+ /// persistent temporary buffer to prevent creating work for garbage collector
+ ///
+ private float[] inputBuffer;
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Buffer to be filled with sample data
+ /// Offset into buffer to start writing to, usually 0
+ /// Number of samples required
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int sampleFramesRequested = count / outputChannelCount;
+ int inputOffset = 0;
+ int sampleFramesRead = 0;
+ // now we must read from all inputs, even if we don't need their data, so they stay in sync
+ foreach (var input in inputs)
+ {
+ int samplesRequired = sampleFramesRequested * input.WaveFormat.Channels;
+ this.inputBuffer = BufferHelpers.Ensure(this.inputBuffer, samplesRequired);
+ int samplesRead = input.Read(inputBuffer, 0, samplesRequired);
+ sampleFramesRead = Math.Max(sampleFramesRead, samplesRead / input.WaveFormat.Channels);
+
+ for (int n = 0; n < input.WaveFormat.Channels; n++)
+ {
+ int inputIndex = inputOffset + n;
+ for (int outputIndex = 0; outputIndex < outputChannelCount; outputIndex++)
+ {
+ if (mappings[outputIndex] == inputIndex)
+ {
+ int inputBufferOffset = n;
+ int outputBufferOffset = offset + outputIndex;
+ int sample = 0;
+ while (sample < sampleFramesRequested && inputBufferOffset < samplesRead)
+ {
+ buffer[outputBufferOffset] = inputBuffer[inputBufferOffset];
+ outputBufferOffset += outputChannelCount;
+ inputBufferOffset += input.WaveFormat.Channels;
+ sample++;
+ }
+ // clear the end
+ while (sample < sampleFramesRequested)
+ {
+ buffer[outputBufferOffset] = 0;
+ outputBufferOffset += outputChannelCount;
+ sample++;
+ }
+ }
+ }
+ }
+ inputOffset += input.WaveFormat.Channels;
+ }
+
+ return sampleFramesRead * outputChannelCount;
+ }
+
+ ///
+ /// The output WaveFormat for this SampleProvider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Connects a specified input channel to an output channel
+ ///
+ /// Input Channel index (zero based). Must be less than InputChannelCount
+ /// Output Channel index (zero based). Must be less than OutputChannelCount
+ public void ConnectInputToOutput(int inputChannel, int outputChannel)
+ {
+ if (inputChannel < 0 || inputChannel >= InputChannelCount)
+ {
+ throw new ArgumentException("Invalid input channel");
+ }
+ if (outputChannel < 0 || outputChannel >= OutputChannelCount)
+ {
+ throw new ArgumentException("Invalid output channel");
+ }
+ mappings[outputChannel] = inputChannel;
+ }
+
+ ///
+ /// The number of input channels. Note that this is not the same as the number of input wave providers. If you pass in
+ /// one stereo and one mono input provider, the number of input channels is three.
+ ///
+ public int InputChannelCount
+ {
+ get { return inputChannelCount; }
+ }
+
+ ///
+ /// The number of output channels, as specified in the constructor.
+ ///
+ public int OutputChannelCount
+ {
+ get { return outputChannelCount; }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/NotifyingSampleProvider.cs b/NAudio/Wave/SampleProviders/NotifyingSampleProvider.cs
new file mode 100644
index 00000000..f3700dd4
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/NotifyingSampleProvider.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Simple class that raises an event on every sample
+ ///
+ public class NotifyingSampleProvider : ISampleProvider, ISampleNotifier
+ {
+ private ISampleProvider source;
+ // try not to give the garbage collector anything to deal with when playing live audio
+ private SampleEventArgs sampleArgs = new SampleEventArgs(0, 0);
+ private int channels;
+
+ ///
+ /// Initializes a new instance of NotifyingSampleProvider
+ ///
+ /// Source Sample Provider
+ public NotifyingSampleProvider(ISampleProvider source)
+ {
+ this.source = source;
+ this.channels = this.WaveFormat.Channels;
+ }
+
+ ///
+ /// WaveFormat
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return source.WaveFormat; }
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples desired
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int sampleCount)
+ {
+ int samplesRead = source.Read(buffer, offset, sampleCount);
+ if (Sample != null)
+ {
+ for (int n = 0; n < samplesRead; n += channels)
+ {
+ sampleArgs.Left = buffer[offset + n];
+ sampleArgs.Right = channels > 1 ? buffer[offset + n + 1] : sampleArgs.Left;
+ Sample(this, sampleArgs);
+ }
+ }
+ return samplesRead;
+ }
+
+ ///
+ /// Sample notifier
+ ///
+ public event EventHandler Sample;
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/OffsetSampleProvider.cs b/NAudio/Wave/SampleProviders/OffsetSampleProvider.cs
new file mode 100644
index 00000000..d97189a3
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/OffsetSampleProvider.cs
@@ -0,0 +1,255 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Allows you to:
+ /// 1. insert a pre-delay of silence before the source begins
+ /// 2. skip over a certain amount of the beginning of the source
+ /// 3. only play a set amount from the source
+ /// 4. insert silence at the end after the source is complete
+ ///
+ public class OffsetSampleProvider : ISampleProvider
+ {
+ private readonly ISampleProvider sourceProvider;
+ private int phase; // 0 = not started yet, 1 = delay, 2 = skip, 3 = take, 4 = lead_out, 5 = end
+ private int phasePos;
+ private int delayBySamples;
+ private int skipOverSamples;
+ private int takeSamples;
+ private int leadOutSamples;
+
+ private int TimeSpanToSamples(TimeSpan time)
+ {
+ var samples = (int)(time.TotalSeconds * WaveFormat.SampleRate) * WaveFormat.Channels;
+ return samples;
+ }
+
+ private TimeSpan SamplesToTimeSpan(int samples)
+ {
+ return TimeSpan.FromSeconds((samples / WaveFormat.Channels) / (double)WaveFormat.SampleRate);
+ }
+
+ ///
+ /// Number of samples of silence to insert before playing source
+ ///
+ public int DelayBySamples
+ {
+ get { return delayBySamples; }
+ set
+ {
+ if (phase != 0)
+ {
+ throw new InvalidOperationException("Can't set DelayBySamples after calling Read");
+ }
+ if (value % WaveFormat.Channels != 0)
+ {
+ throw new ArgumentException("DelayBySamples must be a multiple of WaveFormat.Channels");
+ }
+ delayBySamples = value;
+ }
+ }
+
+ ///
+ /// Amount of silence to insert before playing
+ ///
+ public TimeSpan DelayBy
+ {
+ get { return SamplesToTimeSpan(delayBySamples); }
+ set { delayBySamples = TimeSpanToSamples(value); }
+ }
+
+ ///
+ /// Number of samples in source to discard
+ ///
+ public int SkipOverSamples
+ {
+ get { return skipOverSamples; }
+ set
+ {
+ if (phase != 0)
+ {
+ throw new InvalidOperationException("Can't set SkipOverSamples after calling Read");
+ }
+ if (value % WaveFormat.Channels != 0)
+ {
+ throw new ArgumentException("SkipOverSamples must be a multiple of WaveFormat.Channels");
+ }
+ skipOverSamples = value;
+ }
+ }
+
+ ///
+ /// Amount of audio to skip over from the source before beginning playback
+ ///
+ public TimeSpan SkipOver
+ {
+ get { return SamplesToTimeSpan(skipOverSamples); }
+ set { skipOverSamples = TimeSpanToSamples(value); }
+ }
+
+
+ ///
+ /// Number of samples to read from source (if 0, then read it all)
+ ///
+ public int TakeSamples
+ {
+ get { return takeSamples; }
+ set
+ {
+ if (phase != 0)
+ {
+ throw new InvalidOperationException("Can't set TakeSamples after calling Read");
+ }
+ if (value % WaveFormat.Channels != 0)
+ {
+ throw new ArgumentException("TakeSamples must be a multiple of WaveFormat.Channels");
+ }
+ takeSamples = value;
+ }
+ }
+
+ ///
+ /// Amount of audio to take from the source (TimeSpan.Zero means play to end)
+ ///
+ public TimeSpan Take
+ {
+ get { return SamplesToTimeSpan(takeSamples); }
+ set { takeSamples = TimeSpanToSamples(value); }
+ }
+
+ ///
+ /// Number of samples of silence to insert after playing source
+ ///
+ public int LeadOutSamples
+ {
+ get { return leadOutSamples; }
+ set
+ {
+ if (phase != 0)
+ {
+ throw new InvalidOperationException("Can't set LeadOutSamples after calling Read");
+ }
+ if (value % WaveFormat.Channels != 0)
+ {
+ throw new ArgumentException("LeadOutSamples must be a multiple of WaveFormat.Channels");
+ }
+ leadOutSamples = value;
+ }
+ }
+
+ ///
+ /// Amount of silence to insert after playing source
+ ///
+ public TimeSpan LeadOut
+ {
+ get { return SamplesToTimeSpan(leadOutSamples); }
+ set { leadOutSamples = TimeSpanToSamples(value); }
+ }
+
+ ///
+ /// Creates a new instance of offsetSampleProvider
+ ///
+ /// The Source Sample Provider to read from
+ public OffsetSampleProvider(ISampleProvider sourceProvider)
+ {
+ this.sourceProvider = sourceProvider;
+ }
+
+ ///
+ /// The WaveFormat of this SampleProvider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return sourceProvider.WaveFormat; }
+ }
+
+ ///
+ /// Reads from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset within sample buffer to read to
+ /// Number of samples required
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int samplesRead = 0;
+
+ if (phase == 0) // not started yet
+ {
+ phase++;
+ }
+
+ if (phase == 1) // delay
+ {
+ int delaySamples = Math.Min(count, DelayBySamples - phasePos);
+ for (int n = 0; n < delaySamples; n++)
+ {
+ buffer[offset + n] = 0;
+ }
+ phasePos += delaySamples;
+ samplesRead += delaySamples;
+ if (phasePos >= DelayBySamples)
+ {
+ phase++;
+ phasePos = 0;
+ }
+ }
+
+ if (phase == 2) // skip
+ {
+ if (SkipOverSamples > 0)
+ {
+ var skipBuffer = new float[WaveFormat.SampleRate * WaveFormat.Channels];
+ // skip everything
+ int samplesSkipped = 0;
+ while (samplesSkipped < SkipOverSamples)
+ {
+ int samplesRequired = Math.Min(SkipOverSamples - samplesSkipped, skipBuffer.Length);
+ var read = sourceProvider.Read(skipBuffer, 0, samplesRequired);
+ if (read == 0) // source has ended while still in skip
+ {
+ break;
+ }
+ samplesSkipped += read;
+ }
+ }
+ phase++;
+ phasePos = 0;
+ }
+
+ if (phase == 3) // take
+ {
+ int samplesRequired = count - samplesRead;
+ if (TakeSamples != 0)
+ samplesRequired = Math.Min(samplesRequired, TakeSamples - phasePos);
+ int read = sourceProvider.Read(buffer, offset + samplesRead, samplesRequired);
+ phasePos += read;
+ samplesRead += read;
+ if (read < samplesRequired)
+ {
+ phase++;
+ phasePos = 0;
+ }
+ }
+
+ if (phase == 4) // lead out
+ {
+ int samplesRequired = Math.Min(count - samplesRead, LeadOutSamples - phasePos);
+ for (int n = 0; n < samplesRequired; n++)
+ {
+ buffer[offset + samplesRead + n] = 0;
+ }
+ phasePos += samplesRequired;
+ samplesRead += samplesRequired;
+ if (phasePos >= LeadOutSamples)
+ {
+ phase++;
+ phasePos = 0;
+ }
+ }
+
+ return samplesRead;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/PanningSampleProvider.cs b/NAudio/Wave/SampleProviders/PanningSampleProvider.cs
new file mode 100644
index 00000000..d25c177f
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/PanningSampleProvider.cs
@@ -0,0 +1,220 @@
+using System;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts a mono sample provider to stereo, with a customisable pan strategy
+ ///
+ public class PanningSampleProvider : ISampleProvider
+ {
+ private readonly ISampleProvider source;
+ private float pan;
+ private float leftMultiplier;
+ private float rightMultiplier;
+ private readonly WaveFormat waveFormat;
+ private float[] sourceBuffer;
+ private IPanStrategy panStrategy;
+
+ ///
+ /// Initialises a new instance of the PanningSampleProvider
+ ///
+ /// Source sample provider, must be mono
+ public PanningSampleProvider(ISampleProvider source)
+ {
+ if (source.WaveFormat.Channels != 1)
+ {
+ throw new ArgumentException("Source sample provider must be mono");
+ }
+ this.source = source;
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(source.WaveFormat.SampleRate, 2);
+ this.panStrategy = new SinPanStrategy();
+ }
+
+ ///
+ /// Pan value, must be between -1 (left) and 1 (right)
+ ///
+ public float Pan
+ {
+ get
+ {
+ return this.pan;
+ }
+ set
+ {
+ if (value < -1.0f || value > 1.0f)
+ {
+ throw new ArgumentOutOfRangeException("value", "Pan must be in the range -1 to 1");
+ }
+ this.pan = value;
+ UpdateMultipliers();
+ }
+ }
+
+ ///
+ /// The pan strategy currently in use
+ ///
+ public IPanStrategy PanStrategy
+ {
+ get
+ {
+ return this.panStrategy;
+ }
+ set
+ {
+ this.panStrategy = value;
+ UpdateMultipliers();
+ }
+ }
+
+ private void UpdateMultipliers()
+ {
+ var multipliers = this.panStrategy.GetMultipliers(this.Pan);
+ this.leftMultiplier = multipliers.Left;
+ this.rightMultiplier = multipliers.Right;
+ }
+
+ ///
+ /// The WaveFormat of this sample provider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.waveFormat; }
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples desired
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int sourceSamplesRequired = count / 2;
+ this.sourceBuffer = BufferHelpers.Ensure(this.sourceBuffer, sourceSamplesRequired);
+ int sourceSamplesRead = source.Read(this.sourceBuffer, 0, sourceSamplesRequired);
+ int outIndex = offset;
+ for (int n = 0; n < sourceSamplesRead; n++)
+ {
+ buffer[outIndex++] = leftMultiplier * this.sourceBuffer[n];
+ buffer[outIndex++] = rightMultiplier * this.sourceBuffer[n];
+ }
+ return sourceSamplesRead * 2;
+ }
+ }
+
+ ///
+ /// Pair of floating point values, representing samples or multipliers
+ ///
+ public struct StereoSamplePair
+ {
+ ///
+ /// Left value
+ ///
+ public float Left { get; set; }
+ ///
+ /// Right value
+ ///
+ public float Right { get; set; }
+ }
+
+ ///
+ /// Required Interface for a Panning Strategy
+ ///
+ public interface IPanStrategy
+ {
+ ///
+ /// Gets the left and right multipliers for a given pan value
+ ///
+ /// Pan value from -1 to 1
+ /// Left and right multipliers in a stereo sample pair
+ StereoSamplePair GetMultipliers(float pan);
+ }
+
+ ///
+ /// Simplistic "balance" control - treating the mono input as if it was stereo
+ /// In the centre, both channels full volume. Opposite channel decays linearly
+ /// as balance is turned to to one side
+ ///
+ public class StereoBalanceStrategy : IPanStrategy
+ {
+ ///
+ /// Gets the left and right channel multipliers for this pan value
+ ///
+ /// Pan value, between -1 and 1
+ /// Left and right multipliers
+ public StereoSamplePair GetMultipliers(float pan)
+ {
+ float leftChannel = (pan <= 0) ? 1.0f : ((1 - pan) / 2.0f);
+ float rightChannel = (pan >= 0) ? 1.0f : ((pan + 1) / 2.0f);
+ // Console.WriteLine(pan + ": " + leftChannel + "," + rightChannel);
+ return new StereoSamplePair() { Left = leftChannel, Right = rightChannel };
+ }
+ }
+
+
+ ///
+ /// Square Root Pan, thanks to Yuval Naveh
+ ///
+ public class SquareRootPanStrategy : IPanStrategy
+ {
+ ///
+ /// Gets the left and right channel multipliers for this pan value
+ ///
+ /// Pan value, between -1 and 1
+ /// Left and right multipliers
+ public StereoSamplePair GetMultipliers(float pan)
+ {
+ // -1..+1 -> 1..0
+ float normPan = (-pan + 1) / 2;
+ float leftChannel = (float)Math.Sqrt(normPan);
+ float rightChannel = (float)Math.Sqrt(1 - normPan);
+ // Console.WriteLine(pan + ": " + leftChannel + "," + rightChannel);
+ return new StereoSamplePair() { Left = leftChannel, Right = rightChannel };
+ }
+ }
+
+ ///
+ /// Sinus Pan, thanks to Yuval Naveh
+ ///
+ public class SinPanStrategy : IPanStrategy
+ {
+ private const float HalfPi = (float)Math.PI / 2;
+
+ ///
+ /// Gets the left and right channel multipliers for this pan value
+ ///
+ /// Pan value, between -1 and 1
+ /// Left and right multipliers
+ public StereoSamplePair GetMultipliers(float pan)
+ {
+ // -1..+1 -> 1..0
+ float normPan = (-pan + 1) / 2;
+ float leftChannel = (float)Math.Sin(normPan * HalfPi);
+ float rightChannel = (float)Math.Cos(normPan * HalfPi);
+ // Console.WriteLine(pan + ": " + leftChannel + "," + rightChannel);
+ return new StereoSamplePair() { Left = leftChannel, Right = rightChannel };
+ }
+ }
+
+ ///
+ /// Linear Pan
+ ///
+ public class LinearPanStrategy : IPanStrategy
+ {
+ ///
+ /// Gets the left and right channel multipliers for this pan value
+ ///
+ /// Pan value, between -1 and 1
+ /// Left and right multipliers
+ public StereoSamplePair GetMultipliers(float pan)
+ {
+ // -1..+1 -> 1..0
+ float normPan = (-pan + 1) / 2;
+ float leftChannel = normPan;
+ float rightChannel = 1 - normPan;
+ return new StereoSamplePair() { Left = leftChannel, Right = rightChannel };
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/Pcm16BitToSampleProvider.cs b/NAudio/Wave/SampleProviders/Pcm16BitToSampleProvider.cs
new file mode 100644
index 00000000..121edfbf
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/Pcm16BitToSampleProvider.cs
@@ -0,0 +1,40 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts an IWaveProvider containing 16 bit PCM to an
+ /// ISampleProvider
+ ///
+ public class Pcm16BitToSampleProvider : SampleProviderConverterBase
+ {
+ ///
+ /// Initialises a new instance of Pcm16BitToSampleProvider
+ ///
+ /// Source wave provider
+ public Pcm16BitToSampleProvider(IWaveProvider source)
+ : base(source)
+ {
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Samples required
+ /// Number of samples read
+ public override int Read(float[] buffer, int offset, int count)
+ {
+ int sourceBytesRequired = count * 2;
+ EnsureSourceBuffer(sourceBytesRequired);
+ int bytesRead = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ int outIndex = offset;
+ for(int n = 0; n < bytesRead; n+=2)
+ {
+ buffer[outIndex++] = BitConverter.ToInt16(sourceBuffer, n) / 32768f;
+ }
+ return bytesRead / 2;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/Pcm24BitToSampleProvider.cs b/NAudio/Wave/SampleProviders/Pcm24BitToSampleProvider.cs
new file mode 100644
index 00000000..7a04671a
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/Pcm24BitToSampleProvider.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts an IWaveProvider containing 24 bit PCM to an
+ /// ISampleProvider
+ ///
+ public class Pcm24BitToSampleProvider : SampleProviderConverterBase
+ {
+ ///
+ /// Initialises a new instance of Pcm24BitToSampleProvider
+ ///
+ /// Source Wave Provider
+ public Pcm24BitToSampleProvider(IWaveProvider source)
+ : base(source)
+ {
+
+ }
+
+ ///
+ /// Reads floating point samples from this sample provider
+ ///
+ /// sample buffer
+ /// offset within sample buffer to write to
+ /// number of samples required
+ /// number of samples provided
+ public override int Read(float[] buffer, int offset, int count)
+ {
+ int sourceBytesRequired = count * 3;
+ EnsureSourceBuffer(sourceBytesRequired);
+ int bytesRead = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ int outIndex = offset;
+ for (int n = 0; n < bytesRead; n += 3)
+ {
+ buffer[outIndex++] = (((sbyte)sourceBuffer[n + 2] << 16) | (sourceBuffer[n + 1] << 8) | sourceBuffer[n]) / 8388608f;
+ }
+ return bytesRead / 3;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/Pcm32BitToSampleProvider.cs b/NAudio/Wave/SampleProviders/Pcm32BitToSampleProvider.cs
new file mode 100644
index 00000000..d90a6f59
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/Pcm32BitToSampleProvider.cs
@@ -0,0 +1,42 @@
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts an IWaveProvider containing 32 bit PCM to an
+ /// ISampleProvider
+ ///
+ public class Pcm32BitToSampleProvider : SampleProviderConverterBase
+ {
+ ///
+ /// Initialises a new instance of Pcm32BitToSampleProvider
+ ///
+ /// Source Wave Provider
+ public Pcm32BitToSampleProvider(IWaveProvider source)
+ : base(source)
+ {
+
+ }
+
+ ///
+ /// Reads floating point samples from this sample provider
+ ///
+ /// sample buffer
+ /// offset within sample buffer to write to
+ /// number of samples required
+ /// number of samples provided
+ public override int Read(float[] buffer, int offset, int count)
+ {
+ int sourceBytesRequired = count*4;
+ EnsureSourceBuffer(sourceBytesRequired);
+ int bytesRead = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ int outIndex = offset;
+ for (int n = 0; n < bytesRead; n += 4)
+ {
+ buffer[outIndex++] = (((sbyte) sourceBuffer[n + 3] << 24 |
+ sourceBuffer[n + 2] << 16) |
+ (sourceBuffer[n + 1] << 8) |
+ sourceBuffer[n])/2147483648f;
+ }
+ return bytesRead/4;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/Pcm8BitToSampleProvider.cs b/NAudio/Wave/SampleProviders/Pcm8BitToSampleProvider.cs
new file mode 100644
index 00000000..6870d2d7
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/Pcm8BitToSampleProvider.cs
@@ -0,0 +1,40 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts an IWaveProvider containing 8 bit PCM to an
+ /// ISampleProvider
+ ///
+ public class Pcm8BitToSampleProvider : SampleProviderConverterBase
+ {
+ ///
+ /// Initialises a new instance of Pcm8BitToSampleProvider
+ ///
+ /// Source wave provider
+ public Pcm8BitToSampleProvider(IWaveProvider source) :
+ base(source)
+ {
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples to read
+ /// Number of samples read
+ public override int Read(float[] buffer, int offset, int count)
+ {
+ int sourceBytesRequired = count;
+ EnsureSourceBuffer(sourceBytesRequired);
+ int bytesRead = source.Read(sourceBuffer, 0, sourceBytesRequired);
+ int outIndex = offset;
+ for (int n = 0; n < bytesRead; n++)
+ {
+ buffer[outIndex++] = sourceBuffer[n] / 128f - 1.0f;
+ }
+ return bytesRead;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/SampleChannel.cs b/NAudio/Wave/SampleProviders/SampleChannel.cs
new file mode 100644
index 00000000..df3d59a2
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/SampleChannel.cs
@@ -0,0 +1,86 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Utility class that takes an IWaveProvider input at any bit depth
+ /// and exposes it as an ISampleProvider. Can turn mono inputs into stereo,
+ /// and allows adjusting of volume
+ /// (The eventual successor to WaveChannel32)
+ /// This class also serves as an example of how you can link together several simple
+ /// Sample Providers to form a more useful class.
+ ///
+ public class SampleChannel : ISampleProvider
+ {
+ private readonly VolumeSampleProvider volumeProvider;
+ private readonly MeteringSampleProvider preVolumeMeter;
+ private readonly WaveFormat waveFormat;
+
+ ///
+ /// Initialises a new instance of SampleChannel
+ ///
+ /// Source wave provider, must be PCM or IEEE
+ public SampleChannel(IWaveProvider waveProvider)
+ : this(waveProvider, false)
+ {
+
+ }
+
+ ///
+ /// Initialises a new instance of SampleChannel
+ ///
+ /// Source wave provider, must be PCM or IEEE
+ /// force mono inputs to become stereo
+ public SampleChannel(IWaveProvider waveProvider, bool forceStereo)
+ {
+ ISampleProvider sampleProvider = SampleProviderConverters.ConvertWaveProviderIntoSampleProvider(waveProvider);
+ if (sampleProvider.WaveFormat.Channels == 1 && forceStereo)
+ {
+ sampleProvider = new MonoToStereoSampleProvider(sampleProvider);
+ }
+ this.waveFormat = sampleProvider.WaveFormat;
+ // let's put the meter before the volume (useful for drawing waveforms)
+ this.preVolumeMeter = new MeteringSampleProvider(sampleProvider);
+ this.volumeProvider = new VolumeSampleProvider(preVolumeMeter);
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples desired
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int sampleCount)
+ {
+ return volumeProvider.Read(buffer, offset, sampleCount);
+ }
+
+ ///
+ /// The WaveFormat of this Sample Provider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.waveFormat; }
+ }
+
+ ///
+ /// Allows adjusting the volume, 1.0f = full volume
+ ///
+ public float Volume
+ {
+ get { return volumeProvider.Volume; }
+ set { volumeProvider.Volume = value; }
+ }
+
+ ///
+ /// Raised periodically to inform the user of the max volume
+ /// (before the volume meter)
+ ///
+ public event EventHandler PreVolumeMeter
+ {
+ add { this.preVolumeMeter.StreamVolume += value; }
+ remove { this.preVolumeMeter.StreamVolume -= value; }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/SampleProviderConverterBase.cs b/NAudio/Wave/SampleProviders/SampleProviderConverterBase.cs
new file mode 100644
index 00000000..bbb31a8f
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/SampleProviderConverterBase.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Helper base class for classes converting to ISampleProvider
+ ///
+ public abstract class SampleProviderConverterBase : ISampleProvider
+ {
+ ///
+ /// Source Wave Provider
+ ///
+ protected IWaveProvider source;
+ private WaveFormat waveFormat;
+
+ ///
+ /// Source buffer (to avoid constantly creating small buffers during playback)
+ ///
+ protected byte[] sourceBuffer;
+
+ ///
+ /// Initialises a new instance of SampleProviderConverterBase
+ ///
+ /// Source Wave provider
+ public SampleProviderConverterBase(IWaveProvider source)
+ {
+ this.source = source;
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(source.WaveFormat.SampleRate, source.WaveFormat.Channels);
+ }
+
+ ///
+ /// Wave format of this wave provider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.waveFormat; }
+ }
+
+ ///
+ /// Reads samples from the source wave provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples required
+ /// Number of samples read
+ public abstract int Read(float[] buffer, int offset, int count);
+
+ ///
+ /// Ensure the source buffer exists and is big enough
+ ///
+ /// Bytes required
+ protected void EnsureSourceBuffer(int sourceBytesRequired)
+ {
+ this.sourceBuffer = BufferHelpers.Ensure(this.sourceBuffer, sourceBytesRequired);
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/SampleProviderConverters.cs b/NAudio/Wave/SampleProviders/SampleProviderConverters.cs
new file mode 100644
index 00000000..98f7b200
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/SampleProviderConverters.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Utility class for converting to SampleProvider
+ ///
+ static class SampleProviderConverters
+ {
+ ///
+ /// Helper function to go from IWaveProvider to a SampleProvider
+ /// Must already be PCM or IEEE float
+ ///
+ /// The WaveProvider to convert
+ /// A sample provider
+ public static ISampleProvider ConvertWaveProviderIntoSampleProvider(IWaveProvider waveProvider)
+ {
+ ISampleProvider sampleProvider;
+ if (waveProvider.WaveFormat.Encoding == WaveFormatEncoding.Pcm)
+ {
+ // go to float
+ if (waveProvider.WaveFormat.BitsPerSample == 8)
+ {
+ sampleProvider = new Pcm8BitToSampleProvider(waveProvider);
+ }
+ else if (waveProvider.WaveFormat.BitsPerSample == 16)
+ {
+ sampleProvider = new Pcm16BitToSampleProvider(waveProvider);
+ }
+ else if (waveProvider.WaveFormat.BitsPerSample == 24)
+ {
+ sampleProvider = new Pcm24BitToSampleProvider(waveProvider);
+ }
+ else if (waveProvider.WaveFormat.BitsPerSample == 32)
+ {
+ sampleProvider = new Pcm32BitToSampleProvider(waveProvider);
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported bit depth");
+ }
+ }
+ else if (waveProvider.WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ if (waveProvider.WaveFormat.BitsPerSample == 64)
+ sampleProvider = new WaveToSampleProvider64(waveProvider);
+ else
+ sampleProvider = new WaveToSampleProvider(waveProvider);
+ }
+ else
+ {
+ throw new ArgumentException("Unsupported source encoding");
+ }
+ return sampleProvider;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/SampleToWaveProvider.cs b/NAudio/Wave/SampleProviders/SampleToWaveProvider.cs
new file mode 100644
index 00000000..b3a48cb5
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/SampleToWaveProvider.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Helper class for when you need to convert back to an IWaveProvider from
+ /// an ISampleProvider. Keeps it as IEEE float
+ ///
+ public class SampleToWaveProvider : IWaveProvider
+ {
+ private ISampleProvider source;
+
+ ///
+ /// Initializes a new instance of the WaveProviderFloatToWaveProvider class
+ ///
+ /// Source wave provider
+ public SampleToWaveProvider(ISampleProvider source)
+ {
+ if (source.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ {
+ throw new ArgumentException("Must be already floating point");
+ }
+ this.source = source;
+ }
+
+ ///
+ /// Reads from this provider
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ int samplesNeeded = count / 4;
+ WaveBuffer wb = new WaveBuffer(buffer);
+ int samplesRead = source.Read(wb.FloatBuffer, offset / 4, samplesNeeded);
+ return samplesRead * 4;
+ }
+
+ ///
+ /// The waveformat of this WaveProvider (same as the source)
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return source.WaveFormat; }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/SampleToWaveProvider16.cs b/NAudio/Wave/SampleProviders/SampleToWaveProvider16.cs
new file mode 100644
index 00000000..89807b84
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/SampleToWaveProvider16.cs
@@ -0,0 +1,81 @@
+using System;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts a sample provider to 16 bit PCM, optionally clipping and adjusting volume along the way
+ ///
+ public class SampleToWaveProvider16 : IWaveProvider
+ {
+ private readonly ISampleProvider sourceProvider;
+ private readonly WaveFormat waveFormat;
+ private volatile float volume;
+ private float[] sourceBuffer;
+
+ ///
+ /// Converts from an ISampleProvider (IEEE float) to a 16 bit PCM IWaveProvider.
+ /// Number of channels and sample rate remain unchanged.
+ ///
+ /// The input source provider
+ public SampleToWaveProvider16(ISampleProvider sourceProvider)
+ {
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ throw new ArgumentException("Input source provider must be IEEE float", "sourceProvider");
+ if (sourceProvider.WaveFormat.BitsPerSample != 32)
+ throw new ArgumentException("Input source provider must be 32 bit", "sourceProvider");
+
+ waveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 16, sourceProvider.WaveFormat.Channels);
+
+ this.sourceProvider = sourceProvider;
+ this.volume = 1.0f;
+ }
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// The destination buffer
+ /// Offset into the destination buffer
+ /// Number of bytes read
+ /// Number of bytes read.
+ public int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ int samplesRequired = numBytes / 2;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, samplesRequired);
+ int sourceSamples = sourceProvider.Read(sourceBuffer, 0, samplesRequired);
+ var destWaveBuffer = new WaveBuffer(destBuffer);
+
+ int destOffset = offset / 2;
+ for (int sample = 0; sample < sourceSamples; sample++)
+ {
+ // adjust volume
+ float sample32 = sourceBuffer[sample] * volume;
+ // clip
+ if (sample32 > 1.0f)
+ sample32 = 1.0f;
+ if (sample32 < -1.0f)
+ sample32 = -1.0f;
+ destWaveBuffer.ShortBuffer[destOffset++] = (short)(sample32 * 32767);
+ }
+
+ return sourceSamples * 2;
+ }
+
+ ///
+ ///
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Volume of this channel. 1.0 = full scale
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/SampleToWaveProvider24.cs b/NAudio/Wave/SampleProviders/SampleToWaveProvider24.cs
new file mode 100644
index 00000000..397b9fa8
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/SampleToWaveProvider24.cs
@@ -0,0 +1,85 @@
+using System;
+using NAudio.Utils;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Converts a sample provider to 24 bit PCM, optionally clipping and adjusting volume along the way
+ ///
+ public class SampleToWaveProvider24 : IWaveProvider
+ {
+ private readonly ISampleProvider sourceProvider;
+ private readonly WaveFormat waveFormat;
+ private volatile float volume;
+ private float[] sourceBuffer;
+
+ ///
+ /// Converts from an ISampleProvider (IEEE float) to a 16 bit PCM IWaveProvider.
+ /// Number of channels and sample rate remain unchanged.
+ ///
+ /// The input source provider
+ public SampleToWaveProvider24(ISampleProvider sourceProvider)
+ {
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ throw new ArgumentException("Input source provider must be IEEE float", "sourceProvider");
+ if (sourceProvider.WaveFormat.BitsPerSample != 32)
+ throw new ArgumentException("Input source provider must be 32 bit", "sourceProvider");
+
+ waveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 24, sourceProvider.WaveFormat.Channels);
+
+ this.sourceProvider = sourceProvider;
+ volume = 1.0f;
+ }
+
+ ///
+ /// Reads bytes from this wave stream, clipping if necessary
+ ///
+ /// The destination buffer
+ /// Offset into the destination buffer
+ /// Number of bytes read
+ /// Number of bytes read.
+ public int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ var samplesRequired = numBytes / 3;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, samplesRequired);
+ var sourceSamples = sourceProvider.Read(sourceBuffer, 0, samplesRequired);
+
+ int destOffset = offset;
+ for (var sample = 0; sample < sourceSamples; sample++)
+ {
+ // adjust volume
+ var sample32 = sourceBuffer[sample] * volume;
+ // clip
+ if (sample32 > 1.0f)
+ sample32 = 1.0f;
+ if (sample32 < -1.0f)
+ sample32 = -1.0f;
+
+ var sample24 = (int) (sample32*8388607.0);
+ destBuffer[destOffset++] = (byte)(sample24);
+ destBuffer[destOffset++] = (byte)(sample24 >> 8);
+ destBuffer[destOffset++] = (byte)(sample24 >> 16);
+ }
+
+ return sourceSamples * 3;
+ }
+
+ ///
+ /// The Format of this IWaveProvider
+ ///
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Volume of this channel. 1.0 = full scale, 0.0 to mute
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Wave/SampleProviders/SignalGenerator.cs b/NAudio/Wave/SampleProviders/SignalGenerator.cs
new file mode 100644
index 00000000..2564457b
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/SignalGenerator.cs
@@ -0,0 +1,291 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Signal Generator
+ /// Sin, Square, Triangle, SawTooth, White Noise, Pink Noise, Sweep.
+ ///
+ ///
+ /// Posibility to change ISampleProvider
+ /// Example :
+ /// ---------
+ /// WaveOut _waveOutGene = new WaveOut();
+ /// WaveGenerator wg = new SignalGenerator();
+ /// wg.Type = ...
+ /// wg.Frequency = ...
+ /// wg ...
+ /// _waveOutGene.Init(wg);
+ /// _waveOutGene.Play();
+ ///
+ public class SignalGenerator : ISampleProvider
+ {
+ // Wave format
+ private readonly WaveFormat waveFormat;
+
+ // Random Number for the White Noise & Pink Noise Generator
+ private readonly Random random = new Random();
+
+ private readonly double[] pinkNoiseBuffer = new double[7];
+
+ // Const Math
+ private const double TwoPi = 2*Math.PI;
+
+ // Generator variable
+ private int nSample;
+
+ // Sweep Generator variable
+ private double phi;
+
+ ///
+ /// Initializes a new instance for the Generator (Default :: 44.1Khz, 2 channels, Sinus, Frequency = 440, Gain = 1)
+ ///
+ public SignalGenerator()
+ : this(44100, 2)
+ {
+
+ }
+
+ ///
+ /// Initializes a new instance for the Generator (UserDef SampleRate & Channels)
+ ///
+ /// Desired sample rate
+ /// Number of channels
+ public SignalGenerator(int sampleRate, int channel)
+ {
+ phi = 0;
+ waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channel);
+
+ // Default
+ Type = SignalGeneratorType.Sin;
+ Frequency = 440.0;
+ Gain = 1;
+ PhaseReverse = new bool[channel];
+ SweepLengthSecs = 2;
+ }
+
+ ///
+ /// The waveformat of this WaveProvider (same as the source)
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Frequency for the Generator. (20.0 - 20000.0 Hz)
+ /// Sin, Square, Triangle, SawTooth, Sweep (Start Frequency).
+ ///
+ public double Frequency { get; set; }
+
+ ///
+ /// Return Log of Frequency Start (Read only)
+ ///
+ public double FrequencyLog
+ {
+ get { return Math.Log(Frequency); }
+ }
+
+ ///
+ /// End Frequency for the Sweep Generator. (Start Frequency in Frequency)
+ ///
+ public double FrequencyEnd { get; set; }
+
+ ///
+ /// Return Log of Frequency End (Read only)
+ ///
+ public double FrequencyEndLog
+ {
+ get { return Math.Log(FrequencyEnd); }
+ }
+
+ ///
+ /// Gain for the Generator. (0.0 to 1.0)
+ ///
+ public double Gain { get; set; }
+
+ ///
+ /// Channel PhaseReverse
+ ///
+ public bool[] PhaseReverse { get; private set; }
+
+ ///
+ /// Type of Generator.
+ ///
+ public SignalGeneratorType Type { get; set; }
+
+ ///
+ /// Length Seconds for the Sweep Generator.
+ ///
+ public double SweepLengthSecs { get; set; }
+
+ ///
+ /// Reads from this provider.
+ ///
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int outIndex = offset;
+
+ // Generator current value
+ double multiple;
+ double sampleValue;
+ double sampleSaw;
+
+ // Complete Buffer
+ for (int sampleCount = 0; sampleCount < count/waveFormat.Channels; sampleCount++)
+ {
+ switch (Type)
+ {
+ case SignalGeneratorType.Sin:
+
+ // Sinus Generator
+
+ multiple = TwoPi*Frequency/waveFormat.SampleRate;
+ sampleValue = Gain*Math.Sin(nSample*multiple);
+
+ nSample++;
+
+ break;
+
+
+ case SignalGeneratorType.Square:
+
+ // Square Generator
+
+ multiple = 2*Frequency/waveFormat.SampleRate;
+ sampleSaw = ((nSample*multiple)%2) - 1;
+ sampleValue = sampleSaw > 0 ? Gain : -Gain;
+
+ nSample++;
+ break;
+
+ case SignalGeneratorType.Triangle:
+
+ // Triangle Generator
+
+ multiple = 2*Frequency/waveFormat.SampleRate;
+ sampleSaw = ((nSample*multiple)%2);
+ sampleValue = 2*sampleSaw;
+ if (sampleValue > 1)
+ sampleValue = 2 - sampleValue;
+ if (sampleValue < -1)
+ sampleValue = -2 - sampleValue;
+
+ sampleValue *= Gain;
+
+ nSample++;
+ break;
+
+ case SignalGeneratorType.SawTooth:
+
+ // SawTooth Generator
+
+ multiple = 2*Frequency/waveFormat.SampleRate;
+ sampleSaw = ((nSample*multiple)%2) - 1;
+ sampleValue = Gain*sampleSaw;
+
+ nSample++;
+ break;
+
+ case SignalGeneratorType.White:
+
+ // White Noise Generator
+ sampleValue = (Gain*NextRandomTwo());
+ break;
+
+ case SignalGeneratorType.Pink:
+
+ // Pink Noise Generator
+
+ double white = NextRandomTwo();
+ pinkNoiseBuffer[0] = 0.99886*pinkNoiseBuffer[0] + white*0.0555179;
+ pinkNoiseBuffer[1] = 0.99332*pinkNoiseBuffer[1] + white*0.0750759;
+ pinkNoiseBuffer[2] = 0.96900*pinkNoiseBuffer[2] + white*0.1538520;
+ pinkNoiseBuffer[3] = 0.86650*pinkNoiseBuffer[3] + white*0.3104856;
+ pinkNoiseBuffer[4] = 0.55000*pinkNoiseBuffer[4] + white*0.5329522;
+ pinkNoiseBuffer[5] = -0.7616*pinkNoiseBuffer[5] - white*0.0168980;
+ double pink = pinkNoiseBuffer[0] + pinkNoiseBuffer[1] + pinkNoiseBuffer[2] + pinkNoiseBuffer[3] + pinkNoiseBuffer[4] + pinkNoiseBuffer[5] + pinkNoiseBuffer[6] + white*0.5362;
+ pinkNoiseBuffer[6] = white*0.115926;
+ sampleValue = (Gain*(pink/5));
+ break;
+
+ case SignalGeneratorType.Sweep:
+
+ // Sweep Generator
+ double f = Math.Exp(FrequencyLog + (nSample*(FrequencyEndLog - FrequencyLog))/(SweepLengthSecs*waveFormat.SampleRate));
+
+ multiple = TwoPi*f/waveFormat.SampleRate;
+ phi += multiple;
+ sampleValue = Gain*(Math.Sin(phi));
+ nSample++;
+ if (nSample > SweepLengthSecs*waveFormat.SampleRate)
+ {
+ nSample = 0;
+ phi = 0;
+ }
+ break;
+
+ default:
+ sampleValue = 0.0;
+ break;
+ }
+
+ // Phase Reverse Per Channel
+ for (int i = 0; i < waveFormat.Channels; i++)
+ {
+ if (PhaseReverse[i])
+ buffer[outIndex++] = (float) -sampleValue;
+ else
+ buffer[outIndex++] = (float) sampleValue;
+ }
+ }
+ return count;
+ }
+
+ ///
+ /// Private :: Random for WhiteNoise & Pink Noise (Value form -1 to 1)
+ ///
+ /// Random value from -1 to +1
+ private double NextRandomTwo()
+ {
+ return 2*random.NextDouble() - 1;
+ }
+
+ }
+
+ ///
+ /// Signal Generator type
+ ///
+ public enum SignalGeneratorType
+ {
+ ///
+ /// Pink noise
+ ///
+ Pink,
+ ///
+ /// White noise
+ ///
+ White,
+ ///
+ /// Sweep
+ ///
+ Sweep,
+ ///
+ /// Sine wave
+ ///
+ Sin,
+ ///
+ /// Square wave
+ ///
+ Square,
+ ///
+ /// Triangle Wave
+ ///
+ Triangle,
+ ///
+ /// Sawtooth wave
+ ///
+ SawTooth,
+ }
+
+}
diff --git a/NAudio/Wave/SampleProviders/VolumeSampleProvider.cs b/NAudio/Wave/SampleProviders/VolumeSampleProvider.cs
new file mode 100644
index 00000000..0eb895ea
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/VolumeSampleProvider.cs
@@ -0,0 +1,60 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Very simple sample provider supporting adjustable gain
+ ///
+ public class VolumeSampleProvider : ISampleProvider
+ {
+ private readonly ISampleProvider source;
+ private float volume;
+
+ ///
+ /// Initializes a new instance of VolumeSampleProvider
+ ///
+ /// Source Sample Provider
+ public VolumeSampleProvider(ISampleProvider source)
+ {
+ this.source = source;
+ this.volume = 1.0f;
+ }
+
+ ///
+ /// WaveFormat
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return source.WaveFormat; }
+ }
+
+ ///
+ /// Reads samples from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples desired
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int sampleCount)
+ {
+ int samplesRead = source.Read(buffer, offset, sampleCount);
+ if (volume != 1f)
+ {
+ for (int n = 0; n < sampleCount; n++)
+ {
+ buffer[offset + n] *= volume;
+ }
+ }
+ return samplesRead;
+ }
+
+ ///
+ /// Allows adjusting the volume, 1.0f = full volume
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/WaveToSampleProvider.cs b/NAudio/Wave/SampleProviders/WaveToSampleProvider.cs
new file mode 100644
index 00000000..78a09d02
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/WaveToSampleProvider.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Helper class turning an already 32 bit floating point IWaveProvider
+ /// into an ISampleProvider - hopefully not needed for most applications
+ ///
+ public class WaveToSampleProvider : SampleProviderConverterBase
+ {
+ ///
+ /// Initializes a new instance of the WaveToSampleProvider class
+ ///
+ /// Source wave provider, must be IEEE float
+ public WaveToSampleProvider(IWaveProvider source)
+ : base(source)
+ {
+ if (source.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ {
+ throw new ArgumentException("Must be already floating point");
+ }
+ }
+
+ ///
+ /// Reads from this provider
+ ///
+ public override int Read(float[] buffer, int offset, int count)
+ {
+ int bytesNeeded = count * 4;
+ EnsureSourceBuffer(bytesNeeded);
+ int bytesRead = source.Read(sourceBuffer, 0, bytesNeeded);
+ int samplesRead = bytesRead / 4;
+ int outputIndex = offset;
+ for (int n = 0; n < bytesRead; n+=4)
+ {
+ buffer[outputIndex++] = BitConverter.ToSingle(sourceBuffer, n);
+ }
+ return samplesRead;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/WaveToSampleProvider64.cs b/NAudio/Wave/SampleProviders/WaveToSampleProvider64.cs
new file mode 100644
index 00000000..84fcabe9
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/WaveToSampleProvider64.cs
@@ -0,0 +1,42 @@
+using System;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Helper class turning an already 64 bit floating point IWaveProvider
+ /// into an ISampleProvider - hopefully not needed for most applications
+ ///
+ public class WaveToSampleProvider64 : SampleProviderConverterBase
+ {
+ ///
+ /// Initializes a new instance of the WaveToSampleProvider class
+ ///
+ /// Source wave provider, must be IEEE float
+ public WaveToSampleProvider64(IWaveProvider source)
+ : base(source)
+ {
+ if (source.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ {
+ throw new ArgumentException("Must be already floating point");
+ }
+ }
+
+ ///
+ /// Reads from this provider
+ ///
+ public override int Read(float[] buffer, int offset, int count)
+ {
+ int bytesNeeded = count * 8;
+ EnsureSourceBuffer(bytesNeeded);
+ int bytesRead = source.Read(sourceBuffer, 0, bytesNeeded);
+ int samplesRead = bytesRead / 8;
+ int outputIndex = offset;
+ for (int n = 0; n < bytesRead; n += 8)
+ {
+ long sample64 = BitConverter.ToInt64(sourceBuffer, n);
+ buffer[outputIndex++] = (float)BitConverter.Int64BitsToDouble(sample64);
+ }
+ return samplesRead;
+ }
+ }
+}
diff --git a/NAudio/Wave/SampleProviders/WdlResamplingSampleProvider.cs b/NAudio/Wave/SampleProviders/WdlResamplingSampleProvider.cs
new file mode 100644
index 00000000..5a9130fe
--- /dev/null
+++ b/NAudio/Wave/SampleProviders/WdlResamplingSampleProvider.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Linq;
+using NAudio.Dsp;
+
+namespace NAudio.Wave.SampleProviders
+{
+ ///
+ /// Fully managed resampling sample provider, based on the WDL Resampler
+ ///
+ public class WdlResamplingSampleProvider : ISampleProvider
+ {
+ private readonly WdlResampler resampler;
+ private readonly WaveFormat outFormat;
+ private readonly ISampleProvider source;
+ private readonly int channels;
+
+ ///
+ /// Constructs a new resampler
+ ///
+ /// Source to resample
+ /// Desired output sample rate
+ public WdlResamplingSampleProvider(ISampleProvider source, int newSampleRate)
+ {
+ channels = source.WaveFormat.Channels;
+ outFormat = WaveFormat.CreateIeeeFloatWaveFormat(newSampleRate, channels);
+ this.source = source;
+
+ resampler = new WdlResampler();
+ resampler.SetMode(true, 2, false);
+ resampler.SetFilterParms();
+ resampler.SetFeedMode(false); // output driven
+ resampler.SetRates(source.WaveFormat.SampleRate, newSampleRate);
+ }
+
+ ///
+ /// Reads from this sample provider
+ ///
+ public int Read(float[] buffer, int offset, int count)
+ {
+ float[] inBuffer;
+ int inBufferOffset;
+ int framesRequested = count / channels;
+ int inNeeded = resampler.ResamplePrepare(framesRequested, outFormat.Channels, out inBuffer, out inBufferOffset);
+ int inAvailable = source.Read(inBuffer, inBufferOffset, inNeeded * channels) / channels;
+ int outAvailable = resampler.ResampleOut(buffer, offset, inAvailable, framesRequested, channels);
+ return outAvailable * channels;
+ }
+
+ ///
+ /// Output WaveFormat
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return outFormat; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveExtensionMethods.cs b/NAudio/Wave/WaveExtensionMethods.cs
new file mode 100644
index 00000000..0f1b87da
--- /dev/null
+++ b/NAudio/Wave/WaveExtensionMethods.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Useful extension methods to make switching between WaveAndSampleProvider easier
+ ///
+ public static class WaveExtensionMethods
+ {
+ ///
+ /// Converts a WaveProvider into a SampleProvider (only works for PCM)
+ ///
+ /// WaveProvider to convert
+ ///
+ public static ISampleProvider ToSampleProvider(this IWaveProvider waveProvider)
+ {
+ return SampleProviderConverters.ConvertWaveProviderIntoSampleProvider(waveProvider);
+ }
+
+ ///
+ /// Allows sending a SampleProvider directly to an IWavePlayer without needing to convert
+ /// back to an IWaveProvider
+ ///
+ /// The WavePlayer
+ ///
+ ///
+ public static void Init(this IWavePlayer wavePlayer, ISampleProvider sampleProvider, bool convertTo16Bit = false)
+ {
+ IWaveProvider provider = convertTo16Bit ? (IWaveProvider)new SampleToWaveProvider16(sampleProvider) : new SampleToWaveProvider(sampleProvider);
+ wavePlayer.Init(provider);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/AdpcmWaveFormat.cs b/NAudio/Wave/WaveFormats/AdpcmWaveFormat.cs
new file mode 100644
index 00000000..020ecce8
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/AdpcmWaveFormat.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Microsoft ADPCM
+ /// See http://icculus.org/SDL_sound/downloads/external_documentation/wavecomp.htm
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack=2)]
+ public class AdpcmWaveFormat : WaveFormat
+ {
+ short samplesPerBlock;
+ short numCoeff;
+ // 7 pairs of coefficients
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)]
+ short[] coefficients;
+
+ ///
+ /// Empty constructor needed for marshalling from a pointer
+ ///
+ AdpcmWaveFormat() : this(8000,1)
+ {
+ }
+
+ ///
+ /// Samples per block
+ ///
+ public int SamplesPerBlock
+ {
+ get { return samplesPerBlock; }
+ }
+
+ ///
+ /// Number of coefficients
+ ///
+ public int NumCoefficients
+ {
+ get { return numCoeff; }
+ }
+
+ ///
+ /// Coefficients
+ ///
+ public short[] Coefficients
+ {
+ get { return coefficients; }
+ }
+
+ ///
+ /// Microsoft ADPCM
+ ///
+ /// Sample Rate
+ /// Channels
+ public AdpcmWaveFormat(int sampleRate, int channels) :
+ base(sampleRate,0,channels)
+ {
+ this.waveFormatTag = WaveFormatEncoding.Adpcm;
+
+ // TODO: validate sampleRate, bitsPerSample
+ this.extraSize = 32;
+ switch(this.sampleRate)
+ {
+ case 8000:
+ case 11025:
+ blockAlign = 256;
+ break;
+ case 22050:
+ blockAlign = 512;
+ break;
+ case 44100:
+ default:
+ blockAlign = 1024;
+ break;
+ }
+
+ this.bitsPerSample = 4;
+ this.samplesPerBlock = (short) ((((blockAlign - (7 * channels)) * 8) / (bitsPerSample * channels)) + 2);
+ this.averageBytesPerSecond =
+ ((this.SampleRate * blockAlign) / samplesPerBlock);
+
+ // samplesPerBlock = blockAlign - (7 * channels)) * (2 / channels) + 2;
+
+
+ numCoeff = 7;
+ coefficients = new short[14] {
+ 256,0,512,-256,0,0,192,64,240,0,460,-208,392,-232
+ };
+ }
+
+ ///
+ /// Serializes this wave format
+ ///
+ /// Binary writer
+ public override void Serialize(System.IO.BinaryWriter writer)
+ {
+ base.Serialize(writer);
+ writer.Write(samplesPerBlock);
+ writer.Write(numCoeff);
+ foreach (short coefficient in coefficients)
+ {
+ writer.Write(coefficient);
+ }
+ }
+
+ ///
+ /// String Description of this WaveFormat
+ ///
+ public override string ToString()
+ {
+ return String.Format("Microsoft ADPCM {0} Hz {1} channels {2} bits per sample {3} samples per block",
+ this.SampleRate, this.channels, this.bitsPerSample, this.samplesPerBlock);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/Gsm610WaveFormat.cs b/NAudio/Wave/WaveFormats/Gsm610WaveFormat.cs
new file mode 100644
index 00000000..2321f740
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/Gsm610WaveFormat.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace NAudio.Wave
+{
+ ///
+ /// GSM 610
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
+ public class Gsm610WaveFormat : WaveFormat
+ {
+ private short samplesPerBlock;
+
+ ///
+ /// Creates a GSM 610 WaveFormat
+ /// For now hardcoded to 13kbps
+ ///
+ public Gsm610WaveFormat()
+ {
+ this.waveFormatTag = WaveFormatEncoding.Gsm610;
+ this.channels = 1;
+ this.averageBytesPerSecond = 1625;
+ this.bitsPerSample = 0; // must be zero
+ this.blockAlign = 65;
+ this.sampleRate = 8000;
+
+ this.extraSize = 2;
+ this.samplesPerBlock = 320;
+ }
+
+ ///
+ /// Samples per block
+ ///
+ public short SamplesPerBlock { get { return this.samplesPerBlock; } }
+
+ ///
+ /// Writes this structure to a BinaryWriter
+ ///
+ public override void Serialize(BinaryWriter writer)
+ {
+ base.Serialize(writer);
+ writer.Write(samplesPerBlock);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/ImaAdpcmWaveFormat.cs b/NAudio/Wave/WaveFormats/ImaAdpcmWaveFormat.cs
new file mode 100644
index 00000000..27624137
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/ImaAdpcmWaveFormat.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// IMA/DVI ADPCM Wave Format
+ /// Work in progress
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
+ public class ImaAdpcmWaveFormat : WaveFormat
+ {
+ short samplesPerBlock;
+
+ ///
+ /// parameterless constructor for Marshalling
+ ///
+ ImaAdpcmWaveFormat()
+ {
+ }
+
+ ///
+ /// Creates a new IMA / DVI ADPCM Wave Format
+ ///
+ /// Sample Rate
+ /// Number of channels
+ /// Bits Per Sample
+ public ImaAdpcmWaveFormat(int sampleRate, int channels, int bitsPerSample)
+ {
+ this.waveFormatTag = WaveFormatEncoding.DviAdpcm; // can also be ImaAdpcm - they are the same
+ this.sampleRate = sampleRate;
+ this.channels = (short)channels;
+ this.bitsPerSample = (short)bitsPerSample; // TODO: can be 3 or 4
+ this.extraSize = 2;
+ this.blockAlign = 0; //TODO
+ this.averageBytesPerSecond = 0; //TODO
+ this.samplesPerBlock = 0; // TODO
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/Mp3WaveFormat.cs b/NAudio/Wave/WaveFormats/Mp3WaveFormat.cs
new file mode 100644
index 00000000..d6cf4801
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/Mp3WaveFormat.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// MP3 WaveFormat, MPEGLAYER3WAVEFORMAT from mmreg.h
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
+ public class Mp3WaveFormat : WaveFormat
+ {
+ ///
+ /// Wave format ID (wID)
+ ///
+ public Mp3WaveFormatId id;
+ ///
+ /// Padding flags (fdwFlags)
+ ///
+ public Mp3WaveFormatFlags flags;
+ ///
+ /// Block Size (nBlockSize)
+ ///
+ public ushort blockSize;
+ ///
+ /// Frames per block (nFramesPerBlock)
+ ///
+ public ushort framesPerBlock;
+ ///
+ /// Codec Delay (nCodecDelay)
+ ///
+ public ushort codecDelay;
+
+ private const short Mp3WaveFormatExtraBytes = 12; // MPEGLAYER3_WFX_EXTRA_BYTES
+
+ ///
+ /// Creates a new MP3 WaveFormat
+ ///
+ public Mp3WaveFormat(int sampleRate, int channels, int blockSize, int bitRate)
+ {
+ waveFormatTag = WaveFormatEncoding.MpegLayer3;
+ this.channels = (short)channels;
+ this.averageBytesPerSecond = bitRate / 8;
+ this.bitsPerSample = 0; // must be zero
+ this.blockAlign = 1; // must be 1
+ this.sampleRate = sampleRate;
+
+ this.extraSize = Mp3WaveFormatExtraBytes;
+ this.id = Mp3WaveFormatId.Mpeg;
+ this.flags = Mp3WaveFormatFlags.PaddingIso;
+ this.blockSize = (ushort)blockSize;
+ this.framesPerBlock = 1;
+ this.codecDelay = 0;
+ }
+ }
+
+ ///
+ /// Wave Format Padding Flags
+ ///
+ [Flags]
+ public enum Mp3WaveFormatFlags
+ {
+ ///
+ /// MPEGLAYER3_FLAG_PADDING_ISO
+ ///
+ PaddingIso = 0,
+ ///
+ /// MPEGLAYER3_FLAG_PADDING_ON
+ ///
+ PaddingOn = 1,
+ ///
+ /// MPEGLAYER3_FLAG_PADDING_OFF
+ ///
+ PaddingOff = 2,
+ }
+
+ ///
+ /// Wave Format ID
+ ///
+ public enum Mp3WaveFormatId : ushort
+ {
+ /// MPEGLAYER3_ID_UNKNOWN
+ Unknown = 0,
+ /// MPEGLAYER3_ID_MPEG
+ Mpeg = 1,
+ /// MPEGLAYER3_ID_CONSTANTFRAMESIZE
+ ConstantFrameSize = 2
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/OggWaveFormat.cs b/NAudio/Wave/WaveFormats/OggWaveFormat.cs
new file mode 100644
index 00000000..a64eb2aa
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/OggWaveFormat.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ //http://svn.xiph.org/tags/vorbisacm_20020708/src/vorbisacm/vorbisacm.h
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack=2)]
+ class OggWaveFormat : WaveFormat
+ {
+ //public short cbSize;
+ public uint dwVorbisACMVersion;
+ public uint dwLibVorbisVersion;
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/TrueSpeechWaveFormat.cs b/NAudio/Wave/WaveFormats/TrueSpeechWaveFormat.cs
new file mode 100644
index 00000000..d0c72ebc
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/TrueSpeechWaveFormat.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace NAudio.Wave
+{
+ ///
+ /// DSP Group TrueSpeech
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
+ public class TrueSpeechWaveFormat : WaveFormat
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
+ short[] unknown;
+
+ ///
+ /// DSP Group TrueSpeech WaveFormat
+ ///
+ public TrueSpeechWaveFormat()
+ {
+ this.waveFormatTag = WaveFormatEncoding.DspGroupTrueSpeech;
+ this.channels = 1;
+ this.averageBytesPerSecond = 1067;
+ this.bitsPerSample = 1;
+ this.blockAlign = 32;
+ this.sampleRate = 8000;
+
+ this.extraSize = 32;
+ this.unknown = new short[16];
+ this.unknown[0] = 1;
+ this.unknown[1] = 0xF0;
+ }
+
+ ///
+ /// Writes this structure to a BinaryWriter
+ ///
+ public override void Serialize(BinaryWriter writer)
+ {
+ base.Serialize(writer);
+ foreach (short val in unknown)
+ {
+ writer.Write(val);
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/WaveFormat.cs b/NAudio/Wave/WaveFormats/WaveFormat.cs
new file mode 100644
index 00000000..1a6d2087
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/WaveFormat.cs
@@ -0,0 +1,389 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Represents a Wave file format
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Pack=2)]
+ public class WaveFormat
+ {
+ /// format type
+ protected WaveFormatEncoding waveFormatTag;
+ /// number of channels
+ protected short channels;
+ /// sample rate
+ protected int sampleRate;
+ /// for buffer estimation
+ protected int averageBytesPerSecond;
+ /// block size of data
+ protected short blockAlign;
+ /// number of bits per sample of mono data
+ protected short bitsPerSample;
+ /// number of following bytes
+ protected short extraSize;
+
+ ///
+ /// Creates a new PCM 44.1Khz stereo 16 bit format
+ ///
+ public WaveFormat() : this(44100,16,2)
+ {
+
+ }
+
+ ///
+ /// Creates a new 16 bit wave format with the specified sample
+ /// rate and channel count
+ ///
+ /// Sample Rate
+ /// Number of channels
+ public WaveFormat(int sampleRate, int channels)
+ : this(sampleRate, 16, channels)
+ {
+ }
+
+ ///
+ /// Gets the size of a wave buffer equivalent to the latency in milliseconds.
+ ///
+ /// The milliseconds.
+ ///
+ public int ConvertLatencyToByteSize(int milliseconds)
+ {
+ int bytes = (int) ((AverageBytesPerSecond/1000.0)*milliseconds);
+ if ((bytes%BlockAlign) != 0)
+ {
+ // Return the upper BlockAligned
+ bytes = bytes + BlockAlign - (bytes % BlockAlign);
+ }
+ return bytes;
+ }
+
+ ///
+ /// Creates a WaveFormat with custom members
+ ///
+ /// The encoding
+ /// Sample Rate
+ /// Number of channels
+ /// Average Bytes Per Second
+ /// Block Align
+ /// Bits Per Sample
+ ///
+ public static WaveFormat CreateCustomFormat(WaveFormatEncoding tag, int sampleRate, int channels, int averageBytesPerSecond, int blockAlign, int bitsPerSample)
+ {
+ WaveFormat waveFormat = new WaveFormat();
+ waveFormat.waveFormatTag = tag;
+ waveFormat.channels = (short)channels;
+ waveFormat.sampleRate = sampleRate;
+ waveFormat.averageBytesPerSecond = averageBytesPerSecond;
+ waveFormat.blockAlign = (short)blockAlign;
+ waveFormat.bitsPerSample = (short)bitsPerSample;
+ waveFormat.extraSize = 0;
+ return waveFormat;
+ }
+
+ ///
+ /// Creates an A-law wave format
+ ///
+ /// Sample Rate
+ /// Number of Channels
+ /// Wave Format
+ public static WaveFormat CreateALawFormat(int sampleRate, int channels)
+ {
+ return CreateCustomFormat(WaveFormatEncoding.ALaw, sampleRate, channels, sampleRate * channels, channels, 8);
+ }
+
+ ///
+ /// Creates a Mu-law wave format
+ ///
+ /// Sample Rate
+ /// Number of Channels
+ /// Wave Format
+ public static WaveFormat CreateMuLawFormat(int sampleRate, int channels)
+ {
+ return CreateCustomFormat(WaveFormatEncoding.MuLaw, sampleRate, channels, sampleRate * channels, channels, 8);
+ }
+
+ ///
+ /// Creates a new PCM format with the specified sample rate, bit depth and channels
+ ///
+ public WaveFormat(int rate, int bits, int channels)
+ {
+ if (channels < 1)
+ {
+ throw new ArgumentOutOfRangeException("channels", "Channels must be 1 or greater");
+ }
+ // minimum 16 bytes, sometimes 18 for PCM
+ this.waveFormatTag = WaveFormatEncoding.Pcm;
+ this.channels = (short)channels;
+ this.sampleRate = rate;
+ this.bitsPerSample = (short)bits;
+ this.extraSize = 0;
+
+ this.blockAlign = (short)(channels * (bits / 8));
+ this.averageBytesPerSecond = this.sampleRate * this.blockAlign;
+ }
+
+ ///
+ /// Creates a new 32 bit IEEE floating point wave format
+ ///
+ /// sample rate
+ /// number of channels
+ public static WaveFormat CreateIeeeFloatWaveFormat(int sampleRate, int channels)
+ {
+ WaveFormat wf = new WaveFormat();
+ wf.waveFormatTag = WaveFormatEncoding.IeeeFloat;
+ wf.channels = (short)channels;
+ wf.bitsPerSample = 32;
+ wf.sampleRate = sampleRate;
+ wf.blockAlign = (short) (4*channels);
+ wf.averageBytesPerSecond = sampleRate * wf.blockAlign;
+ wf.extraSize = 0;
+ return wf;
+ }
+
+ ///
+ /// Helper function to retrieve a WaveFormat structure from a pointer
+ ///
+ /// WaveFormat structure
+ ///
+ public static WaveFormat MarshalFromPtr(IntPtr pointer)
+ {
+ WaveFormat waveFormat = (WaveFormat)Marshal.PtrToStructure(pointer, typeof(WaveFormat));
+ switch (waveFormat.Encoding)
+ {
+ case WaveFormatEncoding.Pcm:
+ // can't rely on extra size even being there for PCM so blank it to avoid reading
+ // corrupt data
+ waveFormat.extraSize = 0;
+ break;
+ case WaveFormatEncoding.Extensible:
+ waveFormat = (WaveFormatExtensible)Marshal.PtrToStructure(pointer, typeof(WaveFormatExtensible));
+ break;
+ case WaveFormatEncoding.Adpcm:
+ waveFormat = (AdpcmWaveFormat)Marshal.PtrToStructure(pointer, typeof(AdpcmWaveFormat));
+ break;
+ case WaveFormatEncoding.Gsm610:
+ waveFormat = (Gsm610WaveFormat)Marshal.PtrToStructure(pointer, typeof(Gsm610WaveFormat));
+ break;
+ default:
+ if (waveFormat.ExtraSize > 0)
+ {
+ waveFormat = (WaveFormatExtraData)Marshal.PtrToStructure(pointer, typeof(WaveFormatExtraData));
+ }
+ break;
+ }
+ return waveFormat;
+ }
+
+ ///
+ /// Helper function to marshal WaveFormat to an IntPtr
+ ///
+ /// WaveFormat
+ /// IntPtr to WaveFormat structure (needs to be freed by callee)
+ public static IntPtr MarshalToPtr(WaveFormat format)
+ {
+ int formatSize = Marshal.SizeOf(format);
+ IntPtr formatPointer = Marshal.AllocHGlobal(formatSize);
+ Marshal.StructureToPtr(format, formatPointer, false);
+ return formatPointer;
+ }
+
+ ///
+ /// Reads in a WaveFormat (with extra data) from a fmt chunk (chunk identifier and
+ /// length should already have been read)
+ ///
+ /// Binary reader
+ /// Format chunk length
+ /// A WaveFormatExtraData
+ public static WaveFormat FromFormatChunk(BinaryReader br, int formatChunkLength)
+ {
+ var waveFormat = new WaveFormatExtraData();
+ waveFormat.ReadWaveFormat(br, formatChunkLength);
+ waveFormat.ReadExtraData(br);
+ return waveFormat;
+ }
+
+ private void ReadWaveFormat(BinaryReader br, int formatChunkLength)
+ {
+ if (formatChunkLength < 16)
+ throw new InvalidDataException("Invalid WaveFormat Structure");
+ this.waveFormatTag = (WaveFormatEncoding)br.ReadUInt16();
+ this.channels = br.ReadInt16();
+ this.sampleRate = br.ReadInt32();
+ this.averageBytesPerSecond = br.ReadInt32();
+ this.blockAlign = br.ReadInt16();
+ this.bitsPerSample = br.ReadInt16();
+ if (formatChunkLength > 16)
+ {
+ this.extraSize = br.ReadInt16();
+ if (this.extraSize != formatChunkLength - 18)
+ {
+ Debug.WriteLine("Format chunk mismatch");
+ this.extraSize = (short)(formatChunkLength - 18);
+ }
+ }
+ }
+
+ ///
+ /// Reads a new WaveFormat object from a stream
+ ///
+ /// A binary reader that wraps the stream
+ public WaveFormat(BinaryReader br)
+ {
+ int formatChunkLength = br.ReadInt32();
+ this.ReadWaveFormat(br, formatChunkLength);
+ }
+
+ ///
+ /// Reports this WaveFormat as a string
+ ///
+ /// String describing the wave format
+ public override string ToString()
+ {
+ switch (this.waveFormatTag)
+ {
+ case WaveFormatEncoding.Pcm:
+ case WaveFormatEncoding.Extensible:
+ // extensible just has some extra bits after the PCM header
+ return String.Format("{0} bit PCM: {1}kHz {2} channels",
+ bitsPerSample, sampleRate / 1000, channels);
+ default:
+ return this.waveFormatTag.ToString();
+ }
+ }
+
+ ///
+ /// Compares with another WaveFormat object
+ ///
+ /// Object to compare to
+ /// True if the objects are the same
+ public override bool Equals(object obj)
+ {
+ WaveFormat other = obj as WaveFormat;
+ if(other != null)
+ {
+ return waveFormatTag == other.waveFormatTag &&
+ channels == other.channels &&
+ sampleRate == other.sampleRate &&
+ averageBytesPerSecond == other.averageBytesPerSecond &&
+ blockAlign == other.blockAlign &&
+ bitsPerSample == other.bitsPerSample;
+ }
+ return false;
+ }
+
+ ///
+ /// Provides a Hashcode for this WaveFormat
+ ///
+ /// A hashcode
+ public override int GetHashCode()
+ {
+ return (int) waveFormatTag ^
+ (int) channels ^
+ sampleRate ^
+ averageBytesPerSecond ^
+ (int) blockAlign ^
+ (int) bitsPerSample;
+ }
+
+ ///
+ /// Returns the encoding type used
+ ///
+ public WaveFormatEncoding Encoding
+ {
+ get
+ {
+ return waveFormatTag;
+ }
+ }
+
+ ///
+ /// Writes this WaveFormat object to a stream
+ ///
+ /// the output stream
+ public virtual void Serialize(BinaryWriter writer)
+ {
+ writer.Write((int)(18 + extraSize)); // wave format length
+ writer.Write((short)Encoding);
+ writer.Write((short)Channels);
+ writer.Write((int)SampleRate);
+ writer.Write((int)AverageBytesPerSecond);
+ writer.Write((short)BlockAlign);
+ writer.Write((short)BitsPerSample);
+ writer.Write((short)extraSize);
+ }
+
+ ///
+ /// Returns the number of channels (1=mono,2=stereo etc)
+ ///
+ public int Channels
+ {
+ get
+ {
+ return channels;
+ }
+ }
+
+ ///
+ /// Returns the sample rate (samples per second)
+ ///
+ public int SampleRate
+ {
+ get
+ {
+ return sampleRate;
+ }
+ }
+
+ ///
+ /// Returns the average number of bytes used per second
+ ///
+ public int AverageBytesPerSecond
+ {
+ get
+ {
+ return averageBytesPerSecond;
+ }
+ }
+
+ ///
+ /// Returns the block alignment
+ ///
+ public virtual int BlockAlign
+ {
+ get
+ {
+ return blockAlign;
+ }
+ }
+
+ ///
+ /// Returns the number of bits per sample (usually 16 or 32, sometimes 24 or 8)
+ /// Can be 0 for some codecs
+ ///
+ public int BitsPerSample
+ {
+ get
+ {
+ return bitsPerSample;
+ }
+ }
+
+ ///
+ /// Returns the number of extra bytes used by this waveformat. Often 0,
+ /// except for compressed formats which store extra data after the WAVEFORMATEX header
+ ///
+ public int ExtraSize
+ {
+ get
+ {
+ return extraSize;
+ }
+ }
+
+
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/WaveFormatCustomMarshaler.cs b/NAudio/Wave/WaveFormats/WaveFormatCustomMarshaler.cs
new file mode 100644
index 00000000..5575b7c7
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/WaveFormatCustomMarshaler.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Custom marshaller for WaveFormat structures
+ ///
+ public sealed class WaveFormatCustomMarshaler : ICustomMarshaler
+ {
+ private static WaveFormatCustomMarshaler marshaler = null;
+
+ ///
+ /// Gets the instance of this marshaller
+ ///
+ ///
+ ///
+ public static ICustomMarshaler GetInstance(string cookie)
+ {
+ if (marshaler == null)
+ {
+ marshaler = new WaveFormatCustomMarshaler();
+ }
+ return marshaler;
+ }
+
+ ///
+ /// Clean up managed data
+ ///
+ public void CleanUpManagedData(object ManagedObj)
+ {
+
+ }
+
+ ///
+ /// Clean up native data
+ ///
+ ///
+ public void CleanUpNativeData(IntPtr pNativeData)
+ {
+ Marshal.FreeHGlobal(pNativeData);
+ }
+
+ ///
+ /// Get native data size
+ ///
+ public int GetNativeDataSize()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Marshal managed to native
+ ///
+ public IntPtr MarshalManagedToNative(object ManagedObj)
+ {
+ return WaveFormat.MarshalToPtr((WaveFormat)ManagedObj);
+ }
+
+ ///
+ /// Marshal Native to Managed
+ ///
+ public object MarshalNativeToManaged(IntPtr pNativeData)
+ {
+ return WaveFormat.MarshalFromPtr(pNativeData);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/WaveFormatEncoding.cs b/NAudio/Wave/WaveFormats/WaveFormatEncoding.cs
new file mode 100644
index 00000000..6e38a329
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/WaveFormatEncoding.cs
@@ -0,0 +1,434 @@
+using System;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Summary description for WaveFormatEncoding.
+ ///
+ public enum WaveFormatEncoding : ushort
+ {
+ /// WAVE_FORMAT_UNKNOWN, Microsoft Corporation
+ Unknown = 0x0000,
+
+ /// WAVE_FORMAT_PCM Microsoft Corporation
+ Pcm = 0x0001,
+
+ /// WAVE_FORMAT_ADPCM Microsoft Corporation
+ Adpcm = 0x0002,
+
+ /// WAVE_FORMAT_IEEE_FLOAT Microsoft Corporation
+ IeeeFloat = 0x0003,
+
+ /// WAVE_FORMAT_VSELP Compaq Computer Corp.
+ Vselp = 0x0004,
+
+ /// WAVE_FORMAT_IBM_CVSD IBM Corporation
+ IbmCvsd = 0x0005,
+
+ /// WAVE_FORMAT_ALAW Microsoft Corporation
+ ALaw = 0x0006,
+
+ /// WAVE_FORMAT_MULAW Microsoft Corporation
+ MuLaw = 0x0007,
+
+ /// WAVE_FORMAT_DTS Microsoft Corporation
+ Dts = 0x0008,
+
+ /// WAVE_FORMAT_DRM Microsoft Corporation
+ Drm = 0x0009,
+
+ /// WAVE_FORMAT_WMAVOICE9
+ WmaVoice9 = 0x000A,
+
+ /// WAVE_FORMAT_OKI_ADPCM OKI
+ OkiAdpcm = 0x0010,
+
+ /// WAVE_FORMAT_DVI_ADPCM Intel Corporation
+ DviAdpcm = 0x0011,
+
+ /// WAVE_FORMAT_IMA_ADPCM Intel Corporation
+ ImaAdpcm = DviAdpcm,
+
+ /// WAVE_FORMAT_MEDIASPACE_ADPCM Videologic
+ MediaspaceAdpcm = 0x0012,
+
+ /// WAVE_FORMAT_SIERRA_ADPCM Sierra Semiconductor Corp
+ SierraAdpcm = 0x0013,
+
+ /// WAVE_FORMAT_G723_ADPCM Antex Electronics Corporation
+ G723Adpcm = 0x0014,
+
+ /// WAVE_FORMAT_DIGISTD DSP Solutions, Inc.
+ DigiStd = 0x0015,
+
+ /// WAVE_FORMAT_DIGIFIX DSP Solutions, Inc.
+ DigiFix = 0x0016,
+
+ /// WAVE_FORMAT_DIALOGIC_OKI_ADPCM Dialogic Corporation
+ DialogicOkiAdpcm = 0x0017,
+
+ /// WAVE_FORMAT_MEDIAVISION_ADPCM Media Vision, Inc.
+ MediaVisionAdpcm = 0x0018,
+
+ /// WAVE_FORMAT_CU_CODEC Hewlett-Packard Company
+ CUCodec = 0x0019,
+
+ /// WAVE_FORMAT_YAMAHA_ADPCM Yamaha Corporation of America
+ YamahaAdpcm = 0x0020,
+
+ /// WAVE_FORMAT_SONARC Speech Compression
+ SonarC = 0x0021,
+
+ /// WAVE_FORMAT_DSPGROUP_TRUESPEECH DSP Group, Inc
+ DspGroupTrueSpeech = 0x0022,
+
+ /// WAVE_FORMAT_ECHOSC1 Echo Speech Corporation
+ EchoSpeechCorporation1 = 0x0023,
+
+ /// WAVE_FORMAT_AUDIOFILE_AF36, Virtual Music, Inc.
+ AudioFileAf36 = 0x0024,
+
+ /// WAVE_FORMAT_APTX Audio Processing Technology
+ Aptx = 0x0025,
+
+ /// WAVE_FORMAT_AUDIOFILE_AF10, Virtual Music, Inc.
+ AudioFileAf10 = 0x0026,
+
+ /// WAVE_FORMAT_PROSODY_1612, Aculab plc
+ Prosody1612 = 0x0027,
+
+ /// WAVE_FORMAT_LRC, Merging Technologies S.A.
+ Lrc = 0x0028,
+
+ /// WAVE_FORMAT_DOLBY_AC2, Dolby Laboratories
+ DolbyAc2 = 0x0030,
+
+ /// WAVE_FORMAT_GSM610, Microsoft Corporation
+ Gsm610 = 0x0031,
+
+ /// WAVE_FORMAT_MSNAUDIO, Microsoft Corporation
+ MsnAudio = 0x0032,
+
+ /// WAVE_FORMAT_ANTEX_ADPCME, Antex Electronics Corporation
+ AntexAdpcme = 0x0033,
+
+ /// WAVE_FORMAT_CONTROL_RES_VQLPC, Control Resources Limited
+ ControlResVqlpc = 0x0034,
+
+ /// WAVE_FORMAT_DIGIREAL, DSP Solutions, Inc.
+ DigiReal = 0x0035,
+
+ /// WAVE_FORMAT_DIGIADPCM, DSP Solutions, Inc.
+ DigiAdpcm = 0x0036,
+
+ /// WAVE_FORMAT_CONTROL_RES_CR10, Control Resources Limited
+ ControlResCr10 = 0x0037,
+
+ ///
+ WAVE_FORMAT_NMS_VBXADPCM = 0x0038, // Natural MicroSystems
+ ///
+ WAVE_FORMAT_CS_IMAADPCM = 0x0039, // Crystal Semiconductor IMA ADPCM
+ ///
+ WAVE_FORMAT_ECHOSC3 = 0x003A, // Echo Speech Corporation
+ ///
+ WAVE_FORMAT_ROCKWELL_ADPCM = 0x003B, // Rockwell International
+ ///
+ WAVE_FORMAT_ROCKWELL_DIGITALK = 0x003C, // Rockwell International
+ ///
+ WAVE_FORMAT_XEBEC = 0x003D, // Xebec Multimedia Solutions Limited
+ ///
+ WAVE_FORMAT_G721_ADPCM = 0x0040, // Antex Electronics Corporation
+ ///
+ WAVE_FORMAT_G728_CELP = 0x0041, // Antex Electronics Corporation
+ ///
+ WAVE_FORMAT_MSG723 = 0x0042, // Microsoft Corporation
+ /// WAVE_FORMAT_MPEG, Microsoft Corporation
+ Mpeg = 0x0050,
+
+ ///
+ WAVE_FORMAT_RT24 = 0x0052, // InSoft, Inc.
+ ///
+ WAVE_FORMAT_PAC = 0x0053, // InSoft, Inc.
+ /// WAVE_FORMAT_MPEGLAYER3, ISO/MPEG Layer3 Format Tag
+ MpegLayer3 = 0x0055,
+
+ ///
+ WAVE_FORMAT_LUCENT_G723 = 0x0059, // Lucent Technologies
+ ///
+ WAVE_FORMAT_CIRRUS = 0x0060, // Cirrus Logic
+ ///
+ WAVE_FORMAT_ESPCM = 0x0061, // ESS Technology
+ ///
+ WAVE_FORMAT_VOXWARE = 0x0062, // Voxware Inc
+ ///
+ WAVE_FORMAT_CANOPUS_ATRAC = 0x0063, // Canopus, co., Ltd.
+ ///
+ WAVE_FORMAT_G726_ADPCM = 0x0064, // APICOM
+ ///
+ WAVE_FORMAT_G722_ADPCM = 0x0065, // APICOM
+ ///
+ WAVE_FORMAT_DSAT_DISPLAY = 0x0067, // Microsoft Corporation
+ ///
+ WAVE_FORMAT_VOXWARE_BYTE_ALIGNED = 0x0069, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_AC8 = 0x0070, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_AC10 = 0x0071, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_AC16 = 0x0072, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_AC20 = 0x0073, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_RT24 = 0x0074, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_RT29 = 0x0075, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_RT29HW = 0x0076, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_VR12 = 0x0077, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_VR18 = 0x0078, // Voxware Inc
+ ///
+ WAVE_FORMAT_VOXWARE_TQ40 = 0x0079, // Voxware Inc
+ ///
+ WAVE_FORMAT_SOFTSOUND = 0x0080, // Softsound, Ltd.
+ ///
+ WAVE_FORMAT_VOXWARE_TQ60 = 0x0081, // Voxware Inc
+ ///
+ WAVE_FORMAT_MSRT24 = 0x0082, // Microsoft Corporation
+ ///
+ WAVE_FORMAT_G729A = 0x0083, // AT&T Labs, Inc.
+ ///
+ WAVE_FORMAT_MVI_MVI2 = 0x0084, // Motion Pixels
+ ///
+ WAVE_FORMAT_DF_G726 = 0x0085, // DataFusion Systems (Pty) (Ltd)
+ ///
+ WAVE_FORMAT_DF_GSM610 = 0x0086, // DataFusion Systems (Pty) (Ltd)
+ ///
+ WAVE_FORMAT_ISIAUDIO = 0x0088, // Iterated Systems, Inc.
+ ///
+ WAVE_FORMAT_ONLIVE = 0x0089, // OnLive! Technologies, Inc.
+ ///
+ WAVE_FORMAT_SBC24 = 0x0091, // Siemens Business Communications Sys
+ ///
+ WAVE_FORMAT_DOLBY_AC3_SPDIF = 0x0092, // Sonic Foundry
+ ///
+ WAVE_FORMAT_MEDIASONIC_G723 = 0x0093, // MediaSonic
+ ///
+ WAVE_FORMAT_PROSODY_8KBPS = 0x0094, // Aculab plc
+ ///
+ WAVE_FORMAT_ZYXEL_ADPCM = 0x0097, // ZyXEL Communications, Inc.
+ ///
+ WAVE_FORMAT_PHILIPS_LPCBB = 0x0098, // Philips Speech Processing
+ ///
+ WAVE_FORMAT_PACKED = 0x0099, // Studer Professional Audio AG
+ ///
+ WAVE_FORMAT_MALDEN_PHONYTALK = 0x00A0, // Malden Electronics Ltd.
+ /// WAVE_FORMAT_GSM
+ Gsm = 0x00A1,
+
+ /// WAVE_FORMAT_G729
+ G729 = 0x00A2,
+
+ /// WAVE_FORMAT_G723
+ G723 = 0x00A3,
+
+ /// WAVE_FORMAT_ACELP
+ Acelp = 0x00A4,
+
+ ///
+ /// WAVE_FORMAT_RAW_AAC1
+ ///
+ RawAac = 0x00FF,
+ ///
+ WAVE_FORMAT_RHETOREX_ADPCM = 0x0100, // Rhetorex Inc.
+ ///
+ WAVE_FORMAT_IRAT = 0x0101, // BeCubed Software Inc.
+ ///
+ WAVE_FORMAT_VIVO_G723 = 0x0111, // Vivo Software
+ ///
+ WAVE_FORMAT_VIVO_SIREN = 0x0112, // Vivo Software
+ ///
+ WAVE_FORMAT_DIGITAL_G723 = 0x0123, // Digital Equipment Corporation
+ ///
+ WAVE_FORMAT_SANYO_LD_ADPCM = 0x0125, // Sanyo Electric Co., Ltd.
+ ///
+ WAVE_FORMAT_SIPROLAB_ACEPLNET = 0x0130, // Sipro Lab Telecom Inc.
+ ///
+ WAVE_FORMAT_SIPROLAB_ACELP4800 = 0x0131, // Sipro Lab Telecom Inc.
+ ///
+ WAVE_FORMAT_SIPROLAB_ACELP8V3 = 0x0132, // Sipro Lab Telecom Inc.
+ ///
+ WAVE_FORMAT_SIPROLAB_G729 = 0x0133, // Sipro Lab Telecom Inc.
+ ///
+ WAVE_FORMAT_SIPROLAB_G729A = 0x0134, // Sipro Lab Telecom Inc.
+ ///
+ WAVE_FORMAT_SIPROLAB_KELVIN = 0x0135, // Sipro Lab Telecom Inc.
+ ///
+ WAVE_FORMAT_G726ADPCM = 0x0140, // Dictaphone Corporation
+ ///
+ WAVE_FORMAT_QUALCOMM_PUREVOICE = 0x0150, // Qualcomm, Inc.
+ ///
+ WAVE_FORMAT_QUALCOMM_HALFRATE = 0x0151, // Qualcomm, Inc.
+ ///
+ WAVE_FORMAT_TUBGSM = 0x0155, // Ring Zero Systems, Inc.
+ ///
+ WAVE_FORMAT_MSAUDIO1 = 0x0160, // Microsoft Corporation
+ ///
+ /// Windows Media Audio, WAVE_FORMAT_WMAUDIO2, Microsoft Corporation
+ ///
+ WindowsMediaAudio = 0x0161,
+
+ ///
+ /// Windows Media Audio Professional WAVE_FORMAT_WMAUDIO3, Microsoft Corporation
+ ///
+ WindowsMediaAudioProfessional = 0x0162,
+
+ ///
+ /// Windows Media Audio Lossless, WAVE_FORMAT_WMAUDIO_LOSSLESS
+ ///
+ WindowsMediaAudioLosseless = 0x0163,
+
+ ///
+ /// Windows Media Audio Professional over SPDIF WAVE_FORMAT_WMASPDIF (0x0164)
+ ///
+ WindowsMediaAudioSpdif = 0x0164,
+
+ ///
+ WAVE_FORMAT_UNISYS_NAP_ADPCM = 0x0170, // Unisys Corp.
+ ///
+ WAVE_FORMAT_UNISYS_NAP_ULAW = 0x0171, // Unisys Corp.
+ ///
+ WAVE_FORMAT_UNISYS_NAP_ALAW = 0x0172, // Unisys Corp.
+ ///
+ WAVE_FORMAT_UNISYS_NAP_16K = 0x0173, // Unisys Corp.
+ ///
+ WAVE_FORMAT_CREATIVE_ADPCM = 0x0200, // Creative Labs, Inc
+ ///
+ WAVE_FORMAT_CREATIVE_FASTSPEECH8 = 0x0202, // Creative Labs, Inc
+ ///
+ WAVE_FORMAT_CREATIVE_FASTSPEECH10 = 0x0203, // Creative Labs, Inc
+ ///
+ WAVE_FORMAT_UHER_ADPCM = 0x0210, // UHER informatic GmbH
+ ///
+ WAVE_FORMAT_QUARTERDECK = 0x0220, // Quarterdeck Corporation
+ ///
+ WAVE_FORMAT_ILINK_VC = 0x0230, // I-link Worldwide
+ ///
+ WAVE_FORMAT_RAW_SPORT = 0x0240, // Aureal Semiconductor
+ ///
+ WAVE_FORMAT_ESST_AC3 = 0x0241, // ESS Technology, Inc.
+ ///
+ WAVE_FORMAT_IPI_HSX = 0x0250, // Interactive Products, Inc.
+ ///
+ WAVE_FORMAT_IPI_RPELP = 0x0251, // Interactive Products, Inc.
+ ///
+ WAVE_FORMAT_CS2 = 0x0260, // Consistent Software
+ ///
+ WAVE_FORMAT_SONY_SCX = 0x0270, // Sony Corp.
+ ///
+ WAVE_FORMAT_FM_TOWNS_SND = 0x0300, // Fujitsu Corp.
+ ///
+ WAVE_FORMAT_BTV_DIGITAL = 0x0400, // Brooktree Corporation
+ ///
+ WAVE_FORMAT_QDESIGN_MUSIC = 0x0450, // QDesign Corporation
+ ///
+ WAVE_FORMAT_VME_VMPCM = 0x0680, // AT&T Labs, Inc.
+ ///
+ WAVE_FORMAT_TPC = 0x0681, // AT&T Labs, Inc.
+ ///
+ WAVE_FORMAT_OLIGSM = 0x1000, // Ing C. Olivetti & C., S.p.A.
+ ///
+ WAVE_FORMAT_OLIADPCM = 0x1001, // Ing C. Olivetti & C., S.p.A.
+ ///
+ WAVE_FORMAT_OLICELP = 0x1002, // Ing C. Olivetti & C., S.p.A.
+ ///
+ WAVE_FORMAT_OLISBC = 0x1003, // Ing C. Olivetti & C., S.p.A.
+ ///
+ WAVE_FORMAT_OLIOPR = 0x1004, // Ing C. Olivetti & C., S.p.A.
+ ///
+ WAVE_FORMAT_LH_CODEC = 0x1100, // Lernout & Hauspie
+ ///
+ WAVE_FORMAT_NORRIS = 0x1400, // Norris Communications, Inc.
+ ///
+ WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS = 0x1500, // AT&T Labs, Inc.
+
+ ///
+ /// Advanced Audio Coding (AAC) audio in Audio Data Transport Stream (ADTS) format.
+ /// The format block is a WAVEFORMATEX structure with wFormatTag equal to WAVE_FORMAT_MPEG_ADTS_AAC.
+ ///
+ ///
+ /// The WAVEFORMATEX structure specifies the core AAC-LC sample rate and number of channels,
+ /// prior to applying spectral band replication (SBR) or parametric stereo (PS) tools, if present.
+ /// No additional data is required after the WAVEFORMATEX structure.
+ ///
+ /// http://msdn.microsoft.com/en-us/library/dd317599%28VS.85%29.aspx
+ MPEG_ADTS_AAC = 0x1600,
+
+ ///
+ /// Source wmCodec.h
+ MPEG_RAW_AAC = 0x1601,
+
+ ///
+ /// MPEG-4 audio transport stream with a synchronization layer (LOAS) and a multiplex layer (LATM).
+ /// The format block is a WAVEFORMATEX structure with wFormatTag equal to WAVE_FORMAT_MPEG_LOAS.
+ ///
+ ///
+ /// The WAVEFORMATEX structure specifies the core AAC-LC sample rate and number of channels,
+ /// prior to applying spectral SBR or PS tools, if present.
+ /// No additional data is required after the WAVEFORMATEX structure.
+ ///
+ /// http://msdn.microsoft.com/en-us/library/dd317599%28VS.85%29.aspx
+ MPEG_LOAS = 0x1602,
+
+ /// NOKIA_MPEG_ADTS_AAC
+ /// Source wmCodec.h
+ NOKIA_MPEG_ADTS_AAC = 0x1608,
+
+ /// NOKIA_MPEG_RAW_AAC
+ /// Source wmCodec.h
+ NOKIA_MPEG_RAW_AAC = 0x1609,
+
+ /// VODAFONE_MPEG_ADTS_AAC
+ /// Source wmCodec.h
+ VODAFONE_MPEG_ADTS_AAC = 0x160A,
+
+ /// VODAFONE_MPEG_RAW_AAC
+ /// Source wmCodec.h
+ VODAFONE_MPEG_RAW_AAC = 0x160B,
+
+ ///
+ /// High-Efficiency Advanced Audio Coding (HE-AAC) stream.
+ /// The format block is an HEAACWAVEFORMAT structure.
+ ///
+ /// http://msdn.microsoft.com/en-us/library/dd317599%28VS.85%29.aspx
+ MPEG_HEAAC = 0x1610,
+
+ /// WAVE_FORMAT_DVM
+ WAVE_FORMAT_DVM = 0x2000, // FAST Multimedia AG
+
+ // others - not from MS headers
+ /// WAVE_FORMAT_VORBIS1 "Og" Original stream compatible
+ Vorbis1 = 0x674f,
+
+ /// WAVE_FORMAT_VORBIS2 "Pg" Have independent header
+ Vorbis2 = 0x6750,
+
+ /// WAVE_FORMAT_VORBIS3 "Qg" Have no codebook header
+ Vorbis3 = 0x6751,
+
+ /// WAVE_FORMAT_VORBIS1P "og" Original stream compatible
+ Vorbis1P = 0x676f,
+
+ /// WAVE_FORMAT_VORBIS2P "pg" Have independent headere
+ Vorbis2P = 0x6770,
+
+ /// WAVE_FORMAT_VORBIS3P "qg" Have no codebook header
+ Vorbis3P = 0x6771,
+
+ /// WAVE_FORMAT_EXTENSIBLE
+ Extensible = 0xFFFE, // Microsoft
+ ///
+ WAVE_FORMAT_DEVELOPMENT = 0xFFFF,
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/WaveFormatExtensible.cs b/NAudio/Wave/WaveFormats/WaveFormatExtensible.cs
new file mode 100644
index 00000000..30d66df5
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/WaveFormatExtensible.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using NAudio.Dmo;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveFormatExtensible
+ /// http://www.microsoft.com/whdc/device/audio/multichaud.mspx
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
+ public class WaveFormatExtensible : WaveFormat
+ {
+ short wValidBitsPerSample; // bits of precision, or is wSamplesPerBlock if wBitsPerSample==0
+ int dwChannelMask; // which channels are present in stream
+ Guid subFormat;
+
+ ///
+ /// Parameterless constructor for marshalling
+ ///
+ WaveFormatExtensible()
+ {
+ }
+
+ ///
+ /// Creates a new WaveFormatExtensible for PCM or IEEE
+ ///
+ public WaveFormatExtensible(int rate, int bits, int channels)
+ : base(rate, bits, channels)
+ {
+ waveFormatTag = WaveFormatEncoding.Extensible;
+ extraSize = 22;
+ wValidBitsPerSample = (short) bits;
+ for (int n = 0; n < channels; n++)
+ {
+ dwChannelMask |= (1 << n);
+ }
+ if (bits == 32)
+ {
+ // KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
+ subFormat = AudioMediaSubtypes.MEDIASUBTYPE_IEEE_FLOAT; // new Guid("00000003-0000-0010-8000-00aa00389b71");
+ }
+ else
+ {
+ // KSDATAFORMAT_SUBTYPE_PCM
+ subFormat = AudioMediaSubtypes.MEDIASUBTYPE_PCM; // new Guid("00000001-0000-0010-8000-00aa00389b71");
+ }
+
+ }
+
+ ///
+ /// WaveFormatExtensible for PCM or floating point can be awkward to work with
+ /// This creates a regular WaveFormat structure representing the same audio format
+ ///
+ ///
+ public WaveFormat ToStandardWaveFormat()
+ {
+ if (subFormat == AudioMediaSubtypes.MEDIASUBTYPE_IEEE_FLOAT && bitsPerSample == 32)
+ return CreateIeeeFloatWaveFormat(sampleRate, channels);
+ if (subFormat == AudioMediaSubtypes.MEDIASUBTYPE_PCM)
+ return new WaveFormat(sampleRate,bitsPerSample,channels);
+ throw new InvalidOperationException("Not a recognised PCM or IEEE float format");
+ }
+
+ ///
+ /// SubFormat (may be one of AudioMediaSubtypes)
+ ///
+ public Guid SubFormat { get { return subFormat; } }
+
+ ///
+ /// Serialize
+ ///
+ ///
+ public override void Serialize(System.IO.BinaryWriter writer)
+ {
+ base.Serialize(writer);
+ writer.Write(wValidBitsPerSample);
+ writer.Write(dwChannelMask);
+ byte[] guid = subFormat.ToByteArray();
+ writer.Write(guid, 0, guid.Length);
+ }
+
+ ///
+ /// String representation
+ ///
+ public override string ToString()
+ {
+ return String.Format("{0} wBitsPerSample:{1} dwChannelMask:{2} subFormat:{3} extraSize:{4}",
+ base.ToString(),
+ wValidBitsPerSample,
+ dwChannelMask,
+ subFormat,
+ extraSize);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/WaveFormatExtraData.cs b/NAudio/Wave/WaveFormats/WaveFormatExtraData.cs
new file mode 100644
index 00000000..e82f81ea
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/WaveFormatExtraData.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.IO;
+
+namespace NAudio.Wave
+{
+ ///
+ /// This class used for marshalling from unmanaged code
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
+ public class WaveFormatExtraData : WaveFormat
+ {
+ // try with 100 bytes for now, increase if necessary
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
+ private byte[] extraData = new byte[100];
+
+ ///
+ /// Allows the extra data to be read
+ ///
+ public byte[] ExtraData { get { return extraData; } }
+
+ ///
+ /// parameterless constructor for marshalling
+ ///
+ internal WaveFormatExtraData()
+ {
+ }
+
+ ///
+ /// Reads this structure from a BinaryReader
+ ///
+ public WaveFormatExtraData(BinaryReader reader)
+ : base(reader)
+ {
+ ReadExtraData(reader);
+ }
+
+ internal void ReadExtraData(BinaryReader reader)
+ {
+ if (this.extraSize > 0)
+ {
+ reader.Read(extraData, 0, extraSize);
+ }
+ }
+
+ ///
+ /// Writes this structure to a BinaryWriter
+ ///
+ public override void Serialize(BinaryWriter writer)
+ {
+ base.Serialize(writer);
+ if (extraSize > 0)
+ {
+ writer.Write(extraData, 0, extraSize);
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveFormats/WmaWaveFormat.cs b/NAudio/Wave/WaveFormats/WmaWaveFormat.cs
new file mode 100644
index 00000000..b6bdd29c
--- /dev/null
+++ b/NAudio/Wave/WaveFormats/WmaWaveFormat.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave.WaveFormats
+{
+ ///
+ /// The WMA wave format.
+ /// May not be much use because WMA codec is a DirectShow DMO not an ACM
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 2)]
+ class WmaWaveFormat : WaveFormat
+ {
+ short wValidBitsPerSample; // bits of precision
+ int dwChannelMask; // which channels are present in stream
+ int dwReserved1;
+ int dwReserved2;
+ short wEncodeOptions;
+ short wReserved3;
+
+ public WmaWaveFormat(int sampleRate, int bitsPerSample, int channels)
+ : base(sampleRate,bitsPerSample,channels)
+ {
+ wValidBitsPerSample = (short) bitsPerSample;
+ if (channels == 1)
+ dwChannelMask = 1;
+ else if (channels == 2)
+ dwChannelMask = 3;
+
+ // WMAUDIO3 is Pro
+ this.waveFormatTag = WaveFormatEncoding.WindowsMediaAudio;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveInputs/IWaveIn.cs b/NAudio/Wave/WaveInputs/IWaveIn.cs
new file mode 100644
index 00000000..f836b6a6
--- /dev/null
+++ b/NAudio/Wave/WaveInputs/IWaveIn.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Generic interface for wave recording
+ ///
+ public interface IWaveIn : IDisposable
+ {
+ ///
+ /// Recording WaveFormat
+ ///
+ WaveFormat WaveFormat { get; set; }
+
+ ///
+ /// Start Recording
+ ///
+ void StartRecording();
+
+ ///
+ /// Stop Recording
+ ///
+ void StopRecording();
+
+ ///
+ /// Indicates recorded data is available
+ ///
+ event EventHandler DataAvailable;
+
+ ///
+ /// Indicates that all recorded data has now been received.
+ ///
+ event EventHandler RecordingStopped;
+ }
+}
diff --git a/NAudio/Wave/WaveInputs/WasapiCapture.cs b/NAudio/Wave/WaveInputs/WasapiCapture.cs
new file mode 100644
index 00000000..604ea097
--- /dev/null
+++ b/NAudio/Wave/WaveInputs/WasapiCapture.cs
@@ -0,0 +1,342 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+using System.Threading;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+// for consistency this should be in NAudio.Wave namespace, but left as it is for backwards compatibility
+namespace NAudio.CoreAudioApi
+{
+ ///
+ /// Audio Capture using Wasapi
+ /// See http://msdn.microsoft.com/en-us/library/dd370800%28VS.85%29.aspx
+ ///
+ public class WasapiCapture : IWaveIn
+ {
+ private const long REFTIMES_PER_SEC = 10000000;
+ private const long REFTIMES_PER_MILLISEC = 10000;
+ private volatile bool requestStop;
+ private byte[] recordBuffer;
+ private Thread captureThread;
+ private AudioClient audioClient;
+ private int bytesPerFrame;
+ private WaveFormat waveFormat;
+ private bool initialized;
+ private readonly SynchronizationContext syncContext;
+ private readonly bool isUsingEventSync;
+ private EventWaitHandle frameEventWaitHandle;
+
+ ///
+ /// Indicates recorded data is available
+ ///
+ public event EventHandler DataAvailable;
+
+ ///
+ /// Indicates that all recorded data has now been received.
+ ///
+ public event EventHandler RecordingStopped;
+
+ ///
+ /// Initialises a new instance of the WASAPI capture class
+ ///
+ public WasapiCapture() :
+ this(GetDefaultCaptureDevice())
+ {
+ }
+
+ ///
+ /// Initialises a new instance of the WASAPI capture class
+ ///
+ /// Capture device to use
+ public WasapiCapture(MMDevice captureDevice)
+ : this(captureDevice, false)
+ {
+
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The capture device.
+ /// true if sync is done with event. false use sleep.
+ public WasapiCapture(MMDevice captureDevice, bool useEventSync)
+ {
+ syncContext = SynchronizationContext.Current;
+ audioClient = captureDevice.AudioClient;
+ ShareMode = AudioClientShareMode.Shared;
+ isUsingEventSync = useEventSync;
+
+ waveFormat = audioClient.MixFormat;
+
+ }
+
+ ///
+ /// Share Mode - set before calling StartRecording
+ ///
+ public AudioClientShareMode ShareMode { get; set; }
+
+ ///
+ /// Recording wave format
+ ///
+ public virtual WaveFormat WaveFormat
+ {
+ get
+ {
+ // for convenience, return a WAVEFORMATEX, instead of the real
+ // WAVEFORMATEXTENSIBLE being used
+ var wfe = waveFormat as WaveFormatExtensible;
+ if (wfe != null)
+ {
+ try
+ {
+ return wfe.ToStandardWaveFormat();
+ }
+ catch (InvalidOperationException)
+ {
+ // couldn't convert to a standard format
+ }
+ }
+ return waveFormat;
+ }
+ set { waveFormat = value; }
+ }
+
+ ///
+ /// Gets the default audio capture device
+ ///
+ /// The default audio capture device
+ public static MMDevice GetDefaultCaptureDevice()
+ {
+ var devices = new MMDeviceEnumerator();
+ return devices.GetDefaultAudioEndpoint(DataFlow.Capture, Role.Console);
+ }
+
+ private void InitializeCaptureDevice()
+ {
+ if (initialized)
+ return;
+
+ long requestedDuration = REFTIMES_PER_MILLISEC * 100;
+
+ if (!audioClient.IsFormatSupported(ShareMode, waveFormat))
+ {
+ throw new ArgumentException("Unsupported Wave Format");
+ }
+
+ var streamFlags = GetAudioClientStreamFlags();
+
+ // If using EventSync, setup is specific with shareMode
+ if (isUsingEventSync)
+ {
+ // Init Shared or Exclusive
+ if (ShareMode == AudioClientShareMode.Shared)
+ {
+ // With EventCallBack and Shared, both latencies must be set to 0
+ audioClient.Initialize(ShareMode, AudioClientStreamFlags.EventCallback, requestedDuration, 0,
+ this.waveFormat, Guid.Empty);
+ }
+ else
+ {
+ // With EventCallBack and Exclusive, both latencies must equals
+ audioClient.Initialize(ShareMode, AudioClientStreamFlags.EventCallback, requestedDuration, requestedDuration,
+ this.waveFormat, Guid.Empty);
+ }
+
+ // Create the Wait Event Handle
+ frameEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
+ audioClient.SetEventHandle(frameEventWaitHandle.SafeWaitHandle.DangerousGetHandle());
+ }
+ else
+ {
+ // Normal setup for both sharedMode
+ audioClient.Initialize(ShareMode,
+ streamFlags,
+ requestedDuration,
+ 0,
+ this.waveFormat,
+ Guid.Empty);
+ }
+
+ int bufferFrameCount = audioClient.BufferSize;
+ this.bytesPerFrame = this.waveFormat.Channels * this.waveFormat.BitsPerSample / 8;
+ this.recordBuffer = new byte[bufferFrameCount * bytesPerFrame];
+ Debug.WriteLine(string.Format("record buffer size = {0}", this.recordBuffer.Length));
+
+ initialized = true;
+ }
+
+ ///
+ /// To allow overrides to specify different flags (e.g. loopback)
+ ///
+ protected virtual AudioClientStreamFlags GetAudioClientStreamFlags()
+ {
+ return AudioClientStreamFlags.None;
+ }
+
+ ///
+ /// Start Recording
+ ///
+ public void StartRecording()
+ {
+ if (captureThread != null)
+ {
+ throw new InvalidOperationException("Previous recording still in progress");
+ }
+ InitializeCaptureDevice();
+ ThreadStart start = () => CaptureThread(this.audioClient);
+ this.captureThread = new Thread(start);
+
+ Debug.WriteLine("Thread starting...");
+ this.requestStop = false;
+ this.captureThread.Start();
+ }
+
+ ///
+ /// Stop Recording (requests a stop, wait for RecordingStopped event to know it has finished)
+ ///
+ public void StopRecording()
+ {
+ this.requestStop = true;
+ }
+
+ private void CaptureThread(AudioClient client)
+ {
+ Exception exception = null;
+ try
+ {
+ DoRecording(client);
+ }
+ catch (Exception e)
+ {
+ exception = e;
+ }
+ finally
+ {
+ client.Stop();
+ // don't dispose - the AudioClient only gets disposed when WasapiCapture is disposed
+ }
+ captureThread = null;
+ RaiseRecordingStopped(exception);
+ Debug.WriteLine("Stop wasapi");
+ }
+
+ private void DoRecording(AudioClient client)
+ {
+ Debug.WriteLine(String.Format("Client buffer frame count: {0}", client.BufferSize));
+ int bufferFrameCount = client.BufferSize;
+
+ // Calculate the actual duration of the allocated buffer.
+ long actualDuration = (long)((double)REFTIMES_PER_SEC *
+ bufferFrameCount / waveFormat.SampleRate);
+ int sleepMilliseconds = (int)(actualDuration / REFTIMES_PER_MILLISEC / 2);
+ int waitMilliseconds = (int)(3 * actualDuration / REFTIMES_PER_MILLISEC);
+
+ AudioCaptureClient capture = client.AudioCaptureClient;
+ client.Start();
+
+ if (isUsingEventSync)
+ {
+ Debug.WriteLine(string.Format("wait: {0} ms", waitMilliseconds));
+ }
+ else
+ {
+ Debug.WriteLine(string.Format("sleep: {0} ms", sleepMilliseconds));
+ }
+
+ while (!this.requestStop)
+ {
+ bool readBuffer = true;
+ if (isUsingEventSync)
+ {
+ readBuffer = frameEventWaitHandle.WaitOne(waitMilliseconds, false);
+ }
+ else
+ {
+ Thread.Sleep(sleepMilliseconds);
+ }
+
+ // If still playing and notification is ok
+ if (!this.requestStop && readBuffer)
+ {
+ ReadNextPacket(capture);
+ }
+ }
+ }
+
+ private void RaiseRecordingStopped(Exception e)
+ {
+ var handler = RecordingStopped;
+ if (handler == null) return;
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ this.syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+
+ private void ReadNextPacket(AudioCaptureClient capture)
+ {
+ int packetSize = capture.GetNextPacketSize();
+ int recordBufferOffset = 0;
+ //Debug.WriteLine(string.Format("packet size: {0} samples", packetSize / 4));
+
+ while (packetSize != 0)
+ {
+ int framesAvailable;
+ AudioClientBufferFlags flags;
+ IntPtr buffer = capture.GetBuffer(out framesAvailable, out flags);
+
+ int bytesAvailable = framesAvailable * bytesPerFrame;
+
+ // apparently it is sometimes possible to read more frames than we were expecting?
+ // fix suggested by Michael Feld:
+ int spaceRemaining = Math.Max(0, recordBuffer.Length - recordBufferOffset);
+ if (spaceRemaining < bytesAvailable && recordBufferOffset > 0)
+ {
+ if (DataAvailable != null) DataAvailable(this, new WaveInEventArgs(recordBuffer, recordBufferOffset));
+ recordBufferOffset = 0;
+ }
+
+ // if not silence...
+ if ((flags & AudioClientBufferFlags.Silent) != AudioClientBufferFlags.Silent)
+ {
+ Marshal.Copy(buffer, recordBuffer, recordBufferOffset, bytesAvailable);
+ }
+ else
+ {
+ Array.Clear(recordBuffer, recordBufferOffset, bytesAvailable);
+ }
+ recordBufferOffset += bytesAvailable;
+ capture.ReleaseBuffer(framesAvailable);
+ packetSize = capture.GetNextPacketSize();
+ }
+ if (DataAvailable != null)
+ {
+ DataAvailable(this, new WaveInEventArgs(recordBuffer, recordBufferOffset));
+ }
+ }
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ StopRecording();
+ if (captureThread != null)
+ {
+ captureThread.Join();
+ captureThread = null;
+ }
+ if (audioClient != null)
+ {
+ audioClient.Dispose();
+ audioClient = null;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveInputs/WasapiLoopbackCapture.cs b/NAudio/Wave/WaveInputs/WasapiLoopbackCapture.cs
new file mode 100644
index 00000000..72091783
--- /dev/null
+++ b/NAudio/Wave/WaveInputs/WasapiLoopbackCapture.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.CoreAudioApi;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WASAPI Loopback Capture
+ /// based on a contribution from "Pygmy" - http://naudio.codeplex.com/discussions/203605
+ ///
+ public class WasapiLoopbackCapture : WasapiCapture
+ {
+ ///
+ /// Initialises a new instance of the WASAPI capture class
+ ///
+ public WasapiLoopbackCapture() :
+ this(GetDefaultLoopbackCaptureDevice())
+ {
+ }
+
+ ///
+ /// Initialises a new instance of the WASAPI capture class
+ ///
+ /// Capture device to use
+ public WasapiLoopbackCapture(MMDevice captureDevice) :
+ base(captureDevice)
+ {
+ }
+
+ ///
+ /// Gets the default audio loopback capture device
+ ///
+ /// The default audio loopback capture device
+ public static MMDevice GetDefaultLoopbackCaptureDevice()
+ {
+ MMDeviceEnumerator devices = new MMDeviceEnumerator();
+ return devices.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
+ }
+
+ ///
+ /// Recording wave format
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return base.WaveFormat; }
+ set { throw new InvalidOperationException("WaveFormat cannot be set for WASAPI Loopback Capture"); }
+ }
+
+ ///
+ /// Specify loopback
+ ///
+ protected override AudioClientStreamFlags GetAudioClientStreamFlags()
+ {
+ return AudioClientStreamFlags.Loopback;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveInputs/WaveIn.cs b/NAudio/Wave/WaveInputs/WaveIn.cs
new file mode 100644
index 00000000..5df36def
--- /dev/null
+++ b/NAudio/Wave/WaveInputs/WaveIn.cs
@@ -0,0 +1,308 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Threading;
+using NAudio.Mixer;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Allows recording using the Windows waveIn APIs
+ /// Events are raised as recorded buffers are made available
+ ///
+ public class WaveIn : IWaveIn
+ {
+ private IntPtr waveInHandle;
+ private volatile bool recording;
+ private WaveInBuffer[] buffers;
+ private readonly WaveInterop.WaveCallback callback;
+ private WaveCallbackInfo callbackInfo;
+ private readonly SynchronizationContext syncContext;
+ private int lastReturnedBufferIndex;
+ ///
+ /// Indicates recorded data is available
+ ///
+ public event EventHandler DataAvailable;
+
+ ///
+ /// Indicates that all recorded data has now been received.
+ ///
+ public event EventHandler RecordingStopped;
+
+ ///
+ /// Prepares a Wave input device for recording
+ ///
+ public WaveIn()
+ : this(WaveCallbackInfo.NewWindow())
+ {
+
+ }
+
+ ///
+ /// Creates a WaveIn device using the specified window handle for callbacks
+ ///
+ /// A valid window handle
+ public WaveIn(IntPtr windowHandle)
+ : this(WaveCallbackInfo.ExistingWindow(windowHandle))
+ {
+
+ }
+
+ ///
+ /// Prepares a Wave input device for recording
+ ///
+ public WaveIn(WaveCallbackInfo callbackInfo)
+ {
+ syncContext = SynchronizationContext.Current;
+ if ((callbackInfo.Strategy == WaveCallbackStrategy.NewWindow || callbackInfo.Strategy == WaveCallbackStrategy.ExistingWindow) &&
+ syncContext == null)
+ {
+ throw new InvalidOperationException("Use WaveInEvent to record on a background thread");
+ }
+ DeviceNumber = 0;
+ WaveFormat = new WaveFormat(8000, 16, 1);
+ BufferMilliseconds = 100;
+ NumberOfBuffers = 3;
+ callback = Callback;
+ this.callbackInfo = callbackInfo;
+ callbackInfo.Connect(callback);
+ }
+
+ ///
+ /// Returns the number of Wave In devices available in the system
+ ///
+ public static int DeviceCount
+ {
+ get
+ {
+ return WaveInterop.waveInGetNumDevs();
+ }
+ }
+
+ ///
+ /// Retrieves the capabilities of a waveIn device
+ ///
+ /// Device to test
+ /// The WaveIn device capabilities
+ public static WaveInCapabilities GetCapabilities(int devNumber)
+ {
+ var caps = new WaveInCapabilities();
+ int structSize = Marshal.SizeOf(caps);
+ MmException.Try(WaveInterop.waveInGetDevCaps((IntPtr)devNumber, out caps, structSize), "waveInGetDevCaps");
+ return caps;
+ }
+
+ ///
+ /// Milliseconds for the buffer. Recommended value is 100ms
+ ///
+ public int BufferMilliseconds { get; set; }
+
+ ///
+ /// Number of Buffers to use (usually 2 or 3)
+ ///
+ public int NumberOfBuffers { get; set; }
+
+ ///
+ /// The device number to use
+ ///
+ public int DeviceNumber { get; set; }
+
+ private void CreateBuffers()
+ {
+ // Default to three buffers of 100ms each
+ int bufferSize = BufferMilliseconds * WaveFormat.AverageBytesPerSecond / 1000;
+ if (bufferSize % WaveFormat.BlockAlign != 0)
+ {
+ bufferSize -= bufferSize % WaveFormat.BlockAlign;
+ }
+
+ buffers = new WaveInBuffer[NumberOfBuffers];
+ for (int n = 0; n < buffers.Length; n++)
+ {
+ buffers[n] = new WaveInBuffer(waveInHandle, bufferSize);
+ }
+ }
+
+ ///
+ /// Called when we get a new buffer of recorded data
+ ///
+ private void Callback(IntPtr waveInHandle, WaveInterop.WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
+ {
+ if (message == WaveInterop.WaveMessage.WaveInData)
+ {
+ if (recording)
+ {
+ var hBuffer = (GCHandle)waveHeader.userData;
+ var buffer = (WaveInBuffer)hBuffer.Target;
+ if (buffer == null) return;
+
+ lastReturnedBufferIndex = Array.IndexOf(buffers, buffer);
+ RaiseDataAvailable(buffer);
+ try
+ {
+ buffer.Reuse();
+ }
+ catch (Exception e)
+ {
+ recording = false;
+ RaiseRecordingStopped(e);
+ }
+ }
+
+ }
+ }
+
+ private void RaiseDataAvailable(WaveInBuffer buffer)
+ {
+ var handler = DataAvailable;
+ if (handler != null)
+ {
+ handler(this, new WaveInEventArgs(buffer.Data, buffer.BytesRecorded));
+ }
+ }
+
+ private void RaiseRecordingStopped(Exception e)
+ {
+ var handler = RecordingStopped;
+ if (handler != null)
+ {
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ this.syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+
+ private void OpenWaveInDevice()
+ {
+ CloseWaveInDevice();
+ MmResult result = callbackInfo.WaveInOpen(out waveInHandle, DeviceNumber, WaveFormat, callback);
+ MmException.Try(result, "waveInOpen");
+ CreateBuffers();
+ }
+
+ ///
+ /// Start recording
+ ///
+ public void StartRecording()
+ {
+ if (recording)
+ {
+ throw new InvalidOperationException("Already recording");
+ }
+ OpenWaveInDevice();
+ EnqueueBuffers();
+ MmException.Try(WaveInterop.waveInStart(waveInHandle), "waveInStart");
+ recording = true;
+ }
+
+ private void EnqueueBuffers()
+ {
+ foreach (var buffer in buffers)
+ {
+ if (!buffer.InQueue)
+ {
+ buffer.Reuse();
+ }
+ }
+ }
+
+ ///
+ /// Stop recording
+ ///
+ public void StopRecording()
+ {
+ if (recording)
+ {
+ recording = false;
+ MmException.Try(WaveInterop.waveInStop(waveInHandle), "waveInStop");
+ // report the last buffers, sometimes more than one, so taking care to report them in the right order
+ for (int n = 0; n < buffers.Length; n++)
+ {
+ int index = (n + lastReturnedBufferIndex + 1)%buffers.Length;
+ var buffer = buffers[index];
+ if (buffer.Done)
+ {
+ RaiseDataAvailable(buffer);
+ }
+ }
+ RaiseRecordingStopped(null);
+ }
+ //MmException.Try(WaveInterop.waveInReset(waveInHandle), "waveInReset");
+ // Don't actually close yet so we get the last buffer
+ }
+
+ ///
+ /// WaveFormat we are recording in
+ ///
+ public WaveFormat WaveFormat { get; set; }
+
+ ///
+ /// Dispose pattern
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (recording)
+ StopRecording();
+ CloseWaveInDevice();
+ if (callbackInfo != null)
+ {
+ callbackInfo.Disconnect();
+ callbackInfo = null;
+ }
+ }
+ }
+
+ private void CloseWaveInDevice()
+ {
+ if (waveInHandle == IntPtr.Zero) return;
+ // Some drivers need the reset to properly release buffers
+ WaveInterop.waveInReset(waveInHandle);
+ if (buffers != null)
+ {
+ for (int n = 0; n < buffers.Length; n++)
+ {
+ buffers[n].Dispose();
+ }
+ buffers = null;
+ }
+ WaveInterop.waveInClose(waveInHandle);
+ waveInHandle = IntPtr.Zero;
+
+ }
+
+ ///
+ /// Microphone Level
+ ///
+ public MixerLine GetMixerLine()
+ {
+ // TODO use mixerGetID instead to see if this helps with XP
+ MixerLine mixerLine;
+ if (waveInHandle != IntPtr.Zero)
+ {
+ mixerLine = new MixerLine(this.waveInHandle, 0, MixerFlags.WaveInHandle);
+ }
+ else
+ {
+ mixerLine = new MixerLine((IntPtr)DeviceNumber, 0, MixerFlags.WaveIn);
+ }
+ return mixerLine;
+ }
+
+ ///
+ /// Dispose method
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveInputs/WaveInEvent.cs b/NAudio/Wave/WaveInputs/WaveInEvent.cs
new file mode 100644
index 00000000..b725b8b0
--- /dev/null
+++ b/NAudio/Wave/WaveInputs/WaveInEvent.cs
@@ -0,0 +1,260 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Runtime.InteropServices;
+using NAudio.Mixer;
+using System.Threading;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Recording using waveIn api with event callbacks.
+ /// Use this for recording in non-gui applications
+ /// Events are raised as recorded buffers are made available
+ ///
+ public class WaveInEvent : IWaveIn
+ {
+ private readonly AutoResetEvent callbackEvent;
+ private readonly SynchronizationContext syncContext;
+ private IntPtr waveInHandle;
+ private volatile bool recording;
+ private WaveInBuffer[] buffers;
+
+ ///
+ /// Indicates recorded data is available
+ ///
+ public event EventHandler DataAvailable;
+
+ ///
+ /// Indicates that all recorded data has now been received.
+ ///
+ public event EventHandler RecordingStopped;
+
+ ///
+ /// Prepares a Wave input device for recording
+ ///
+ public WaveInEvent()
+ {
+ this.callbackEvent = new AutoResetEvent(false);
+ this.syncContext = SynchronizationContext.Current;
+ this.DeviceNumber = 0;
+ this.WaveFormat = new WaveFormat(8000, 16, 1);
+ this.BufferMilliseconds = 100;
+ this.NumberOfBuffers = 3;
+ }
+
+ ///
+ /// Returns the number of Wave In devices available in the system
+ ///
+ public static int DeviceCount
+ {
+ get
+ {
+ return WaveInterop.waveInGetNumDevs();
+ }
+ }
+
+ ///
+ /// Retrieves the capabilities of a waveIn device
+ ///
+ /// Device to test
+ /// The WaveIn device capabilities
+ public static WaveInCapabilities GetCapabilities(int devNumber)
+ {
+ WaveInCapabilities caps = new WaveInCapabilities();
+ int structSize = Marshal.SizeOf(caps);
+ MmException.Try(WaveInterop.waveInGetDevCaps((IntPtr)devNumber, out caps, structSize), "waveInGetDevCaps");
+ return caps;
+ }
+
+ ///
+ /// Milliseconds for the buffer. Recommended value is 100ms
+ ///
+ public int BufferMilliseconds { get; set; }
+
+ ///
+ /// Number of Buffers to use (usually 2 or 3)
+ ///
+ public int NumberOfBuffers { get; set; }
+
+ ///
+ /// The device number to use
+ ///
+ public int DeviceNumber { get; set; }
+
+ private void CreateBuffers()
+ {
+ // Default to three buffers of 100ms each
+ int bufferSize = BufferMilliseconds * WaveFormat.AverageBytesPerSecond / 1000;
+ if (bufferSize % WaveFormat.BlockAlign != 0)
+ {
+ bufferSize -= bufferSize % WaveFormat.BlockAlign;
+ }
+
+ buffers = new WaveInBuffer[NumberOfBuffers];
+ for (int n = 0; n < buffers.Length; n++)
+ {
+ buffers[n] = new WaveInBuffer(waveInHandle, bufferSize);
+ }
+ }
+
+ private void OpenWaveInDevice()
+ {
+ CloseWaveInDevice();
+ MmResult result = WaveInterop.waveInOpenWindow(out waveInHandle, (IntPtr)DeviceNumber, WaveFormat,
+ callbackEvent.SafeWaitHandle.DangerousGetHandle(), IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackEvent);
+ MmException.Try(result, "waveInOpen");
+ CreateBuffers();
+ }
+
+ ///
+ /// Start recording
+ ///
+ public void StartRecording()
+ {
+ if (recording)
+ throw new InvalidOperationException("Already recording");
+ OpenWaveInDevice();
+ MmException.Try(WaveInterop.waveInStart(waveInHandle), "waveInStart");
+ recording = true;
+ ThreadPool.QueueUserWorkItem((state) => RecordThread(), null);
+ }
+
+ private void RecordThread()
+ {
+ Exception exception = null;
+ try
+ {
+ DoRecording();
+ }
+ catch (Exception e)
+ {
+ exception = e;
+ }
+ finally
+ {
+ recording = false;
+ RaiseRecordingStoppedEvent(exception);
+ }
+ }
+
+ private void DoRecording()
+ {
+ foreach (var buffer in buffers)
+ {
+ if (!buffer.InQueue)
+ {
+ buffer.Reuse();
+ }
+ }
+ while (recording)
+ {
+ if (callbackEvent.WaitOne())
+ {
+ // requeue any buffers returned to us
+ if (recording)
+ {
+ foreach (var buffer in buffers)
+ {
+ if (buffer.Done)
+ {
+ if (DataAvailable != null)
+ {
+ DataAvailable(this, new WaveInEventArgs(buffer.Data, buffer.BytesRecorded));
+ }
+ buffer.Reuse();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void RaiseRecordingStoppedEvent(Exception e)
+ {
+ var handler = RecordingStopped;
+ if (handler != null)
+ {
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ this.syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+ ///
+ /// Stop recording
+ ///
+ public void StopRecording()
+ {
+ recording = false;
+ this.callbackEvent.Set(); // signal the thread to exit
+ MmException.Try(WaveInterop.waveInStop(waveInHandle), "waveInStop");
+ }
+
+ ///
+ /// WaveFormat we are recording in
+ ///
+ public WaveFormat WaveFormat { get; set; }
+
+ ///
+ /// Dispose pattern
+ ///
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (recording)
+ StopRecording();
+
+ CloseWaveInDevice();
+ }
+ }
+
+ private void CloseWaveInDevice()
+ {
+ // Some drivers need the reset to properly release buffers
+ WaveInterop.waveInReset(waveInHandle);
+ if (buffers != null)
+ {
+ for (int n = 0; n < buffers.Length; n++)
+ {
+ buffers[n].Dispose();
+ }
+ buffers = null;
+ }
+ WaveInterop.waveInClose(waveInHandle);
+ waveInHandle = IntPtr.Zero;
+ }
+
+ ///
+ /// Microphone Level
+ ///
+ public MixerLine GetMixerLine()
+ {
+ // TODO use mixerGetID instead to see if this helps with XP
+ MixerLine mixerLine;
+ if (waveInHandle != IntPtr.Zero)
+ {
+ mixerLine = new MixerLine(this.waveInHandle, 0, MixerFlags.WaveInHandle);
+ }
+ else
+ {
+ mixerLine = new MixerLine((IntPtr)DeviceNumber, 0, MixerFlags.WaveIn);
+ }
+ return mixerLine;
+ }
+
+ ///
+ /// Dispose method
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/AiffFileWriter.cs b/NAudio/Wave/WaveOutputs/AiffFileWriter.cs
new file mode 100644
index 00000000..a281a0f0
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/AiffFileWriter.cs
@@ -0,0 +1,376 @@
+using System;
+using System.IO;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// This class writes audio data to a .aif file on disk
+ ///
+ public class AiffFileWriter : Stream
+ {
+ private Stream outStream;
+ private BinaryWriter writer;
+ private long dataSizePos;
+ private long commSampleCountPos;
+ private int dataChunkSize = 8;
+ private WaveFormat format;
+ private string filename;
+
+ ///
+ /// Creates an Aiff file by reading all the data from a WaveProvider
+ /// BEWARE: the WaveProvider MUST return 0 from its Read method when it is finished,
+ /// or the Aiff File will grow indefinitely.
+ ///
+ /// The filename to use
+ /// The source WaveProvider
+ public static void CreateAiffFile(string filename, WaveStream sourceProvider)
+ {
+ using (var writer = new AiffFileWriter(filename, sourceProvider.WaveFormat))
+ {
+ byte[] buffer = new byte[16384];
+
+ while (sourceProvider.Position < sourceProvider.Length)
+ {
+ int count = Math.Min((int)(sourceProvider.Length - sourceProvider.Position), buffer.Length);
+ int bytesRead = sourceProvider.Read(buffer, 0, count);
+
+ if (bytesRead == 0)
+ {
+ // end of source provider
+ break;
+ }
+
+ writer.Write(buffer, 0, bytesRead);
+ }
+ }
+ }
+
+ ///
+ /// AiffFileWriter that actually writes to a stream
+ ///
+ /// Stream to be written to
+ /// Wave format to use
+ public AiffFileWriter(Stream outStream, WaveFormat format)
+ {
+ this.outStream = outStream;
+ this.format = format;
+ this.writer = new BinaryWriter(outStream, System.Text.Encoding.UTF8);
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("FORM"));
+ this.writer.Write((int)0); // placeholder
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("AIFF"));
+
+ CreateCommChunk();
+ WriteSsndChunkHeader();
+ }
+
+ ///
+ /// Creates a new AiffFileWriter
+ ///
+ /// The filename to write to
+ /// The Wave Format of the output data
+ public AiffFileWriter(string filename, WaveFormat format)
+ : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), format)
+ {
+ this.filename = filename;
+ }
+
+ private void WriteSsndChunkHeader()
+ {
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("SSND"));
+ dataSizePos = this.outStream.Position;
+ this.writer.Write((int)0); // placeholder
+ this.writer.Write((int)0); // zero offset
+ this.writer.Write(SwapEndian((int)format.BlockAlign));
+ }
+
+ private byte[] SwapEndian(short n)
+ {
+ return new byte[] { (byte)(n >> 8), (byte)(n & 0xff) };
+ }
+
+ private byte[] SwapEndian(int n)
+ {
+ return new byte[] { (byte)((n >> 24) & 0xff), (byte)((n >> 16) & 0xff), (byte)((n >> 8) & 0xff), (byte)(n & 0xff), };
+ }
+
+ private void CreateCommChunk()
+ {
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("COMM"));
+ this.writer.Write(SwapEndian((int)18));
+ this.writer.Write(SwapEndian((short)format.Channels));
+ commSampleCountPos = this.outStream.Position; ;
+ this.writer.Write((int)0); // placeholder for total number of samples
+ this.writer.Write(SwapEndian((short)format.BitsPerSample));
+ this.writer.Write(IEEE.ConvertToIeeeExtended(format.SampleRate));
+ }
+
+ ///
+ /// The aiff file name or null if not applicable
+ ///
+ public string Filename
+ {
+ get { return filename; }
+ }
+
+ ///
+ /// Number of bytes of audio in the data chunk
+ ///
+ public override long Length
+ {
+ get { return dataChunkSize; }
+ }
+
+ ///
+ /// WaveFormat of this aiff file
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return format; }
+ }
+
+ ///
+ /// Returns false: Cannot read from a AiffFileWriter
+ ///
+ public override bool CanRead
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Returns true: Can write to a AiffFileWriter
+ ///
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Returns false: Cannot seek within a AiffFileWriter
+ ///
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Read is not supported for a AiffFileWriter
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new InvalidOperationException("Cannot read from an AiffFileWriter");
+ }
+
+ ///
+ /// Seek is not supported for a AiffFileWriter
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new InvalidOperationException("Cannot seek within an AiffFileWriter");
+ }
+
+ ///
+ /// SetLength is not supported for AiffFileWriter
+ ///
+ ///
+ public override void SetLength(long value)
+ {
+ throw new InvalidOperationException("Cannot set length of an AiffFileWriter");
+ }
+
+ ///
+ /// Gets the Position in the AiffFile (i.e. number of bytes written so far)
+ ///
+ public override long Position
+ {
+ get { return dataChunkSize; }
+ set { throw new InvalidOperationException("Repositioning an AiffFileWriter is not supported"); }
+ }
+
+ ///
+ /// Appends bytes to the AiffFile (assumes they are already in the correct format)
+ ///
+ /// the buffer containing the wave data
+ /// the offset from which to start writing
+ /// the number of bytes to write
+ public override void Write(byte[] data, int offset, int count)
+ {
+ byte[] swappedData = new byte[data.Length];
+
+ int align = format.BitsPerSample / 8;
+
+ for (int i = 0; i < data.Length; i++)
+ {
+ int pos = (int)Math.Floor((double)i / align) * align + (align - (i % align) - 1);
+ swappedData[i] = data[pos];
+ }
+
+ outStream.Write(swappedData, offset, count);
+ dataChunkSize += count;
+ }
+
+ private byte[] value24 = new byte[3]; // keep this around to save us creating it every time
+
+ ///
+ /// Writes a single sample to the Aiff file
+ ///
+ /// the sample to write (assumed floating point with 1.0f as max value)
+ public void WriteSample(float sample)
+ {
+ if (WaveFormat.BitsPerSample == 16)
+ {
+ writer.Write(SwapEndian((Int16)(Int16.MaxValue * sample)));
+ dataChunkSize += 2;
+ }
+ else if (WaveFormat.BitsPerSample == 24)
+ {
+ var value = BitConverter.GetBytes((Int32)(Int32.MaxValue * sample));
+ value24[2] = value[1];
+ value24[1] = value[2];
+ value24[0] = value[3];
+ writer.Write(value24);
+ dataChunkSize += 3;
+ }
+ else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == NAudio.Wave.WaveFormatEncoding.Extensible)
+ {
+ writer.Write(SwapEndian(UInt16.MaxValue * (Int32)sample));
+ dataChunkSize += 4;
+ }
+ else
+ {
+ throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported");
+ }
+ }
+
+ ///
+ /// Writes 32 bit floating point samples to the Aiff file
+ /// They will be converted to the appropriate bit depth depending on the WaveFormat of the AIF file
+ ///
+ /// The buffer containing the floating point samples
+ /// The offset from which to start writing
+ /// The number of floating point samples to write
+ public void WriteSamples(float[] samples, int offset, int count)
+ {
+ for (int n = 0; n < count; n++)
+ {
+ WriteSample(samples[offset + n]);
+ }
+ }
+
+ ///
+ /// Writes 16 bit samples to the Aiff file
+ ///
+ /// The buffer containing the 16 bit samples
+ /// The offset from which to start writing
+ /// The number of 16 bit samples to write
+ public void WriteSamples(short[] samples, int offset, int count)
+ {
+ // 16 bit PCM data
+ if (WaveFormat.BitsPerSample == 16)
+ {
+ for (int sample = 0; sample < count; sample++)
+ {
+ writer.Write(SwapEndian(samples[sample + offset]));
+ }
+ dataChunkSize += (count * 2);
+ }
+ // 24 bit PCM data
+ else if (WaveFormat.BitsPerSample == 24)
+ {
+ byte[] value;
+ for (int sample = 0; sample < count; sample++)
+ {
+ value = BitConverter.GetBytes(UInt16.MaxValue * (Int32)samples[sample + offset]);
+ value24[2] = value[1];
+ value24[1] = value[2];
+ value24[0] = value[3];
+ writer.Write(value24);
+ }
+ dataChunkSize += (count * 3);
+ }
+ // 32 bit PCM data
+ else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible)
+ {
+ for (int sample = 0; sample < count; sample++)
+ {
+ writer.Write(SwapEndian(UInt16.MaxValue * (Int32)samples[sample + offset]));
+ }
+ dataChunkSize += (count * 4);
+ }
+ else
+ {
+ throw new InvalidOperationException("Only 16, 24 or 32 bit PCM audio data supported");
+ }
+ }
+
+ ///
+ /// Ensures data is written to disk
+ ///
+ public override void Flush()
+ {
+ writer.Flush();
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Actually performs the close,making sure the header contains the correct data
+ ///
+ /// True if called from Dispose
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (outStream != null)
+ {
+ try
+ {
+ UpdateHeader(writer);
+ }
+ finally
+ {
+ // in a finally block as we don't want the FileStream to run its disposer in
+ // the GC thread if the code above caused an IOException (e.g. due to disk full)
+ outStream.Close(); // will close the underlying base stream
+ outStream = null;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Updates the header with file size information
+ ///
+ protected virtual void UpdateHeader(BinaryWriter writer)
+ {
+ this.Flush();
+ writer.Seek(4, SeekOrigin.Begin);
+ writer.Write(SwapEndian((int)(outStream.Length - 8)));
+ UpdateCommChunk(writer);
+ UpdateSsndChunk(writer);
+ }
+
+ private void UpdateCommChunk(BinaryWriter writer)
+ {
+ writer.Seek((int)commSampleCountPos, SeekOrigin.Begin);
+ writer.Write(SwapEndian((int)(dataChunkSize * 8 / format.BitsPerSample / format.Channels)));
+ }
+
+ private void UpdateSsndChunk(BinaryWriter writer)
+ {
+ writer.Seek((int)dataSizePos, SeekOrigin.Begin);
+ writer.Write(SwapEndian((int)dataChunkSize));
+ }
+
+ ///
+ /// Finaliser - should only be called if the user forgot to close this AiffFileWriter
+ ///
+ ~AiffFileWriter()
+ {
+ System.Diagnostics.Debug.Assert(false, "AiffFileWriter was not disposed");
+ Dispose(false);
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/AsioAudioAvailableEventArgs.cs b/NAudio/Wave/WaveOutputs/AsioAudioAvailableEventArgs.cs
new file mode 100644
index 00000000..eec50b50
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/AsioAudioAvailableEventArgs.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave.Asio;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Raised when ASIO data has been recorded.
+ /// It is important to handle this as quickly as possible as it is in the buffer callback
+ ///
+ public class AsioAudioAvailableEventArgs : EventArgs
+ {
+ ///
+ /// Initialises a new instance of AsioAudioAvailableEventArgs
+ ///
+ /// Pointers to the ASIO buffers for each channel
+ /// Pointers to the ASIO buffers for each channel
+ /// Number of samples in each buffer
+ /// Audio format within each buffer
+ public AsioAudioAvailableEventArgs(IntPtr[] inputBuffers, IntPtr[] outputBuffers, int samplesPerBuffer, AsioSampleType asioSampleType)
+ {
+ InputBuffers = inputBuffers;
+ OutputBuffers = outputBuffers;
+ SamplesPerBuffer = samplesPerBuffer;
+ AsioSampleType = asioSampleType;
+ }
+
+ ///
+ /// Pointer to a buffer per input channel
+ ///
+ public IntPtr[] InputBuffers { get; private set; }
+
+ ///
+ /// Pointer to a buffer per output channel
+ /// Allows you to write directly to the output buffers
+ /// If you do so, set SamplesPerBuffer = true,
+ /// and make sure all buffers are written to with valid data
+ ///
+ public IntPtr[] OutputBuffers { get; private set; }
+
+ ///
+ /// Set to true if you have written to the output buffers
+ /// If so, AsioOut will not read from its source
+ ///
+ public bool WrittenToOutputBuffers { get; set; }
+
+ ///
+ /// Number of samples in each buffer
+ ///
+ public int SamplesPerBuffer { get; private set; }
+
+ ///
+ /// Converts all the recorded audio into a buffer of 32 bit floating point samples, interleaved by channel
+ ///
+ /// The samples as 32 bit floating point, interleaved
+ public int GetAsInterleavedSamples(float[] samples)
+ {
+ int channels = InputBuffers.Length;
+ if (samples.Length < SamplesPerBuffer*channels) throw new ArgumentException("Buffer not big enough");
+ int index = 0;
+ unsafe
+ {
+ if (AsioSampleType == AsioSampleType.Int32LSB)
+ {
+ for (int n = 0; n < SamplesPerBuffer; n++)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ {
+ samples[index++] = *((int*)InputBuffers[ch] + n) / (float)Int32.MaxValue;
+ }
+ }
+ }
+ else if (AsioSampleType == AsioSampleType.Int16LSB)
+ {
+ for (int n = 0; n < SamplesPerBuffer; n++)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ {
+ samples[index++] = *((short*)InputBuffers[ch] + n) / (float)Int16.MaxValue;
+ }
+ }
+ }
+ else if (AsioSampleType == AsioSampleType.Int24LSB)
+ {
+ for (int n = 0; n < SamplesPerBuffer; n++)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ {
+ byte *pSample = ((byte*)InputBuffers[ch] + n * 3);
+
+ //int sample = *pSample + *(pSample+1) << 8 + (sbyte)*(pSample+2) << 16;
+ int sample = pSample[0] | (pSample[1] << 8) | ((sbyte)pSample[2] << 16);
+ samples[index++] = sample / 8388608.0f;
+ }
+ }
+ }
+ else if (AsioSampleType == AsioSampleType.Float32LSB)
+ {
+ for (int n = 0; n < SamplesPerBuffer; n++)
+ {
+ for (int ch = 0; ch < channels; ch++)
+ {
+ samples[index++] = *((float*)InputBuffers[ch] + n);
+ }
+ }
+ }
+ else
+ {
+ throw new NotImplementedException(String.Format("ASIO Sample Type {0} not supported", AsioSampleType));
+ }
+ }
+ return SamplesPerBuffer*channels;
+ }
+
+ ///
+ /// Audio format within each buffer
+ /// Most commonly this will be one of, Int32LSB, Int16LSB, Int24LSB or Float32LSB
+ ///
+ public AsioSampleType AsioSampleType { get; private set; }
+
+ ///
+ /// Gets as interleaved samples, allocating a float array
+ ///
+ /// The samples as 32 bit floating point values
+ [Obsolete("Better performance if you use the overload that takes an array, and reuse the same one")]
+ public float[] GetAsInterleavedSamples()
+ {
+ int channels = InputBuffers.Length;
+ var samples = new float[SamplesPerBuffer*channels];
+ GetAsInterleavedSamples(samples);
+ return samples;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/AsioOut.cs b/NAudio/Wave/WaveOutputs/AsioOut.cs
new file mode 100644
index 00000000..ee16d981
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/AsioOut.cs
@@ -0,0 +1,407 @@
+using System;
+using NAudio.Wave.Asio;
+using System.Threading;
+
+namespace NAudio.Wave
+{
+ ///
+ /// ASIO Out Player. New implementation using an internal C# binding.
+ ///
+ /// This implementation is only supporting Short16Bit and Float32Bit formats and is optimized
+ /// for 2 outputs channels .
+ /// SampleRate is supported only if ASIODriver is supporting it
+ ///
+ /// This implementation is probably the first ASIODriver binding fully implemented in C#!
+ ///
+ /// Original Contributor: Mark Heath
+ /// New Contributor to C# binding : Alexandre Mutel - email: alexandre_mutel at yahoo.fr
+ ///
+ public class AsioOut : IWavePlayer
+ {
+ private ASIODriverExt driver;
+ private IWaveProvider sourceStream;
+ private PlaybackState playbackState;
+ private int nbSamples;
+ private byte[] waveBuffer;
+ private ASIOSampleConvertor.SampleConvertor convertor;
+ private readonly string driverName;
+
+ private readonly SynchronizationContext syncContext;
+
+ ///
+ /// Playback Stopped
+ ///
+ public event EventHandler PlaybackStopped;
+
+ ///
+ /// When recording, fires whenever recorded audio is available
+ ///
+ public event EventHandler AudioAvailable;
+
+ ///
+ /// Initializes a new instance of the class with the first
+ /// available ASIO Driver.
+ ///
+ public AsioOut()
+ : this(0)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the driver name.
+ ///
+ /// Name of the device.
+ public AsioOut(String driverName)
+ {
+ this.syncContext = SynchronizationContext.Current;
+ InitFromName(driverName);
+ }
+
+ ///
+ /// Opens an ASIO output device
+ ///
+ /// Device number (zero based)
+ public AsioOut(int driverIndex)
+ {
+ this.syncContext = SynchronizationContext.Current;
+ String[] names = GetDriverNames();
+ if (names.Length == 0)
+ {
+ throw new ArgumentException("There is no ASIO Driver installed on your system");
+ }
+ if (driverIndex < 0 || driverIndex > names.Length)
+ {
+ throw new ArgumentException(String.Format("Invalid device number. Must be in the range [0,{0}]", names.Length));
+ }
+ this.driverName = names[driverIndex];
+ InitFromName(this.driverName);
+ }
+
+ ///
+ /// Releases unmanaged resources and performs other cleanup operations before the
+ /// is reclaimed by garbage collection.
+ ///
+ ~AsioOut()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (driver != null)
+ {
+ if (playbackState != PlaybackState.Stopped)
+ {
+ driver.Stop();
+ }
+ driver.ReleaseDriver();
+ driver = null;
+ }
+ }
+
+ ///
+ /// Gets the names of the installed ASIO Driver.
+ ///
+ /// an array of driver names
+ public static String[] GetDriverNames()
+ {
+ return ASIODriver.GetASIODriverNames();
+ }
+
+ ///
+ /// Determines whether ASIO is supported.
+ ///
+ ///
+ /// true if ASIO is supported; otherwise, false .
+ ///
+ public static bool isSupported()
+ {
+ return GetDriverNames().Length > 0;
+ }
+
+ ///
+ /// Inits the driver from the asio driver name.
+ ///
+ /// Name of the driver.
+ private void InitFromName(String driverName)
+ {
+ // Get the basic driver
+ ASIODriver basicDriver = ASIODriver.GetASIODriverByName(driverName);
+
+ // Instantiate the extended driver
+ driver = new ASIODriverExt(basicDriver);
+ this.ChannelOffset = 0;
+ }
+
+ ///
+ /// Shows the control panel
+ ///
+ public void ShowControlPanel()
+ {
+ driver.ShowControlPanel();
+ }
+
+ ///
+ /// Starts playback
+ ///
+ public void Play()
+ {
+ if (playbackState != PlaybackState.Playing)
+ {
+ playbackState = PlaybackState.Playing;
+ driver.Start();
+ }
+ }
+
+ ///
+ /// Stops playback
+ ///
+ public void Stop()
+ {
+ playbackState = PlaybackState.Stopped;
+ driver.Stop();
+ RaisePlaybackStopped(null);
+ }
+
+ ///
+ /// Pauses playback
+ ///
+ public void Pause()
+ {
+ playbackState = PlaybackState.Paused;
+ driver.Stop();
+ }
+
+ ///
+ /// Initialises to play
+ ///
+ /// Source wave provider
+ public void Init(IWaveProvider waveProvider)
+ {
+ this.InitRecordAndPlayback(waveProvider, 0, -1);
+ }
+
+ ///
+ /// Initialises to play, with optional recording
+ ///
+ /// Source wave provider - set to null for record only
+ /// Number of channels to record
+ /// Specify sample rate here if only recording, ignored otherwise
+ public void InitRecordAndPlayback(IWaveProvider waveProvider, int recordChannels, int recordOnlySampleRate)
+ {
+ if (this.sourceStream != null)
+ {
+ throw new InvalidOperationException("Already initialised this instance of AsioOut - dispose and create a new one");
+ }
+ int desiredSampleRate = waveProvider != null ? waveProvider.WaveFormat.SampleRate : recordOnlySampleRate;
+
+ if (waveProvider != null)
+ {
+ sourceStream = waveProvider;
+
+ this.NumberOfOutputChannels = waveProvider.WaveFormat.Channels;
+
+ // Select the correct sample convertor from WaveFormat -> ASIOFormat
+ convertor = ASIOSampleConvertor.SelectSampleConvertor(waveProvider.WaveFormat, driver.Capabilities.OutputChannelInfos[0].type);
+ }
+ else
+ {
+ this.NumberOfOutputChannels = 0;
+ }
+
+
+ if (!driver.IsSampleRateSupported(desiredSampleRate))
+ {
+ throw new ArgumentException("SampleRate is not supported");
+ }
+ if (driver.Capabilities.SampleRate != desiredSampleRate)
+ {
+ driver.SetSampleRate(desiredSampleRate);
+ }
+
+ // Plug the callback
+ driver.FillBufferCallback = driver_BufferUpdate;
+
+ this.NumberOfInputChannels = recordChannels;
+ // Used Prefered size of ASIO Buffer
+ nbSamples = driver.CreateBuffers(NumberOfOutputChannels, NumberOfInputChannels, false);
+ driver.SetChannelOffset(ChannelOffset, InputChannelOffset); // will throw an exception if channel offset is too high
+
+ if (waveProvider != null)
+ {
+ // make a buffer big enough to read enough from the sourceStream to fill the ASIO buffers
+ waveBuffer = new byte[nbSamples * NumberOfOutputChannels * waveProvider.WaveFormat.BitsPerSample / 8];
+ }
+ }
+
+ ///
+ /// driver buffer update callback to fill the wave buffer.
+ ///
+ /// The input channels.
+ /// The output channels.
+ void driver_BufferUpdate(IntPtr[] inputChannels, IntPtr[] outputChannels)
+ {
+ if (this.NumberOfInputChannels > 0)
+ {
+ var audioAvailable = AudioAvailable;
+ if (audioAvailable != null)
+ {
+ var args = new AsioAudioAvailableEventArgs(inputChannels, outputChannels, nbSamples,
+ driver.Capabilities.InputChannelInfos[0].type);
+ audioAvailable(this, args);
+ if (args.WrittenToOutputBuffers)
+ return;
+ }
+ }
+
+ if (this.NumberOfOutputChannels > 0)
+ {
+ int read = sourceStream.Read(waveBuffer, 0, waveBuffer.Length);
+ if (read < waveBuffer.Length)
+ {
+ // we have stopped
+ }
+
+ // Call the convertor
+ unsafe
+ {
+ // TODO : check if it's better to lock the buffer at initialization?
+ fixed (void* pBuffer = &waveBuffer[0])
+ {
+ convertor(new IntPtr(pBuffer), outputChannels, NumberOfOutputChannels, nbSamples);
+ }
+ }
+
+ if (read == 0)
+ {
+ Stop();
+ }
+ }
+ }
+
+ ///
+ /// Gets the latency (in ms) of the playback driver
+ ///
+ public int PlaybackLatency
+ {
+ get
+ {
+ int latency, temp;
+ driver.Driver.GetLatencies(out temp, out latency);
+ return latency;
+ }
+ }
+
+ ///
+ /// Playback State
+ ///
+ public PlaybackState PlaybackState
+ {
+ get { return playbackState; }
+ }
+
+ ///
+ /// Driver Name
+ ///
+ public string DriverName
+ {
+ get { return this.driverName; }
+ }
+
+ ///
+ /// The number of output channels we are currently using for playback
+ /// (Must be less than or equal to DriverOutputChannelCount)
+ ///
+ public int NumberOfOutputChannels { get; private set; }
+
+ ///
+ /// The number of input channels we are currently recording from
+ /// (Must be less than or equal to DriverInputChannelCount)
+ ///
+ public int NumberOfInputChannels { get; private set; }
+
+ ///
+ /// The maximum number of input channels this ASIO driver supports
+ ///
+ public int DriverInputChannelCount { get { return driver.Capabilities.NbInputChannels; } }
+
+ ///
+ /// The maximum number of output channels this ASIO driver supports
+ ///
+ public int DriverOutputChannelCount { get { return driver.Capabilities.NbOutputChannels; } }
+
+ ///
+ /// By default the first channel on the input WaveProvider is sent to the first ASIO output.
+ /// This option sends it to the specified channel number.
+ /// Warning: make sure you don't set it higher than the number of available output channels -
+ /// the number of source channels.
+ /// n.b. Future NAudio may modify this
+ ///
+ public int ChannelOffset { get; set; }
+
+ ///
+ /// Input channel offset (used when recording), allowing you to choose to record from just one
+ /// specific input rather than them all
+ ///
+ public int InputChannelOffset { get; set; }
+
+ ///
+ /// Sets the volume (1.0 is unity gain)
+ /// Not supported for ASIO Out. Set the volume on the input stream instead
+ ///
+ [Obsolete("this function will be removed in a future NAudio as ASIO does not support setting the volume on the device")]
+ public float Volume
+ {
+ get
+ {
+ return 1.0f;
+ }
+ set
+ {
+ if (value != 1.0f)
+ {
+ throw new InvalidOperationException("AsioOut does not support setting the device volume");
+ }
+ }
+ }
+
+ private void RaisePlaybackStopped(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ this.syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+
+ ///
+ /// Get the input channel name
+ ///
+ /// channel index (zero based)
+ /// channel name
+ public string AsioInputChannelName(int channel)
+ {
+ return channel > DriverInputChannelCount ? "" : driver.Capabilities.InputChannelInfos[channel].name;
+ }
+
+ ///
+ /// Get the output channel name
+ ///
+ /// channel index (zero based)
+ /// channel name
+ public string AsioOutputChannelName(int channel)
+ {
+ return channel > DriverOutputChannelCount ? "" : driver.Capabilities.OutputChannelInfos[channel].name;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/CueWaveFileWriter.cs b/NAudio/Wave/WaveOutputs/CueWaveFileWriter.cs
new file mode 100644
index 00000000..b40377c4
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/CueWaveFileWriter.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.Wave
+{
+ ///
+ /// A wave file writer that adds cue support
+ ///
+ public class CueWaveFileWriter : WaveFileWriter
+ {
+ private CueList cues = null;
+
+ ///
+ /// Writes a wave file, including a cues chunk
+ ///
+ public CueWaveFileWriter(string fileName, WaveFormat waveFormat)
+ : base (fileName, waveFormat)
+ {
+ }
+
+ ///
+ /// Adds a cue to the Wave file
+ ///
+ /// Sample position
+ /// Label text
+ public void AddCue(int position, string label)
+ {
+ if (cues == null)
+ {
+ cues = new CueList();
+ }
+ cues.Add(new Cue(position, label));
+ }
+
+ private void WriteCues(BinaryWriter w)
+ {
+ // write the cue chunks to the end of the stream
+ if (cues != null)
+ {
+ byte[] cueChunks = cues.GetRIFFChunks();
+ int cueChunksSize = cueChunks.Length;
+ w.Seek(0, SeekOrigin.End);
+
+ if (w.BaseStream.Length % 2 == 1)
+ {
+ w.Write((Byte)0x00);
+ }
+
+ w.Write(cues.GetRIFFChunks(), 0, cueChunksSize);
+ w.Seek(4, SeekOrigin.Begin);
+ w.Write((int)(w.BaseStream.Length - 8));
+ }
+ }
+
+ ///
+ /// Updates the header, and writes the cues out
+ ///
+ protected override void UpdateHeader(BinaryWriter writer)
+ {
+ base.UpdateHeader(writer);
+ WriteCues(writer);
+ }
+ }
+}
+
diff --git a/NAudio/Wave/WaveOutputs/DirectSoundOut.cs b/NAudio/Wave/WaveOutputs/DirectSoundOut.cs
new file mode 100644
index 00000000..07eba526
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/DirectSoundOut.cs
@@ -0,0 +1,881 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Threading;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace NAudio.Wave
+{
+ ///
+ /// NativeDirectSoundOut using DirectSound COM interop.
+ /// Contact author: Alexandre Mutel - alexandre_mutel at yahoo.fr
+ /// Modified by: Graham "Gee" Plumb
+ ///
+ public class DirectSoundOut : IWavePlayer
+ {
+ ///
+ /// Playback Stopped
+ ///
+ public event EventHandler PlaybackStopped;
+
+ private PlaybackState playbackState;
+ private WaveFormat waveFormat;
+ private int samplesTotalSize;
+ private int samplesFrameSize;
+ private int nextSamplesWriteIndex;
+ private int desiredLatency;
+ private Guid device;
+ private byte[] samples;
+ private IWaveProvider waveStream = null;
+ private IDirectSound directSound = null;
+ private IDirectSoundBuffer primarySoundBuffer = null;
+ private IDirectSoundBuffer secondaryBuffer = null;
+ private EventWaitHandle frameEventWaitHandle1;
+ private EventWaitHandle frameEventWaitHandle2;
+ private EventWaitHandle endEventWaitHandle;
+ private Thread notifyThread;
+ private SynchronizationContext syncContext;
+ private long bytesPlayed;
+
+ // Used purely for locking
+ private Object m_LockObject = new Object();
+
+ ///
+ /// Gets the DirectSound output devices in the system
+ ///
+ public static IEnumerable Devices
+ {
+ get {
+ devices = new List();
+ DirectSoundEnumerate(new DSEnumCallback(EnumCallback), IntPtr.Zero);
+ return devices;
+ }
+ }
+
+ private static List devices;
+
+ private static bool EnumCallback(IntPtr lpGuid, IntPtr lpcstrDescription, IntPtr lpcstrModule, IntPtr lpContext)
+ {
+ var device = new DirectSoundDeviceInfo();
+ if (lpGuid == IntPtr.Zero)
+ {
+ device.Guid = Guid.Empty;
+ }
+ else
+ {
+ byte[] guidBytes = new byte[16];
+ Marshal.Copy(lpGuid, guidBytes, 0, 16);
+ device.Guid = new Guid(guidBytes);
+ }
+ device.Description = Marshal.PtrToStringAnsi(lpcstrDescription);
+ if (lpcstrModule != null)
+ {
+ device.ModuleName = Marshal.PtrToStringAnsi(lpcstrModule);
+ }
+ devices.Add(device);
+ return true;
+ }
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DirectSoundOut()
+ : this(DSDEVID_DefaultPlayback)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DirectSoundOut(Guid device)
+ : this(device, 40)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public DirectSoundOut(int latency)
+ : this(DSDEVID_DefaultPlayback, latency)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// (40ms seems to work under Vista).
+ ///
+ /// The latency.
+ /// Selected device
+ public DirectSoundOut(Guid device, int latency)
+ {
+ if (device == Guid.Empty)
+ {
+ device = DSDEVID_DefaultPlayback;
+ }
+ this.device = device;
+ this.desiredLatency = latency;
+ this.syncContext = SynchronizationContext.Current;
+ }
+
+ ///
+ /// Releases unmanaged resources and performs other cleanup operations before the
+ /// is reclaimed by garbage collection.
+ ///
+ ~DirectSoundOut()
+ {
+ Dispose();
+ }
+
+ ///
+ /// Begin playback
+ ///
+ public void Play()
+ {
+ if (playbackState == PlaybackState.Stopped)
+ {
+ // -------------------------------------------------------------------------------------
+ // Thread that process samples
+ // -------------------------------------------------------------------------------------
+ notifyThread = new Thread(new ThreadStart(PlaybackThreadFunc));
+ // put this back to highest when we are confident we don't have any bugs in the thread proc
+ notifyThread.Priority = ThreadPriority.Normal;
+ notifyThread.IsBackground = true;
+ notifyThread.Start();
+ }
+
+ lock (m_LockObject)
+ {
+ playbackState = PlaybackState.Playing;
+ }
+ }
+
+ ///
+ /// Stop playback
+ ///
+ public void Stop()
+ {
+ // Try and tidy up nicely
+ if (Monitor.TryEnter(m_LockObject, 50))
+ {
+ playbackState = PlaybackState.Stopped;
+ Monitor.Exit(m_LockObject);
+ }
+ else
+ {
+ // No joy - abort the thread!
+ if (notifyThread != null)
+ {
+ notifyThread.Abort();
+ notifyThread = null;
+ }
+ }
+ }
+
+ ///
+ /// Pause Playback
+ ///
+ public void Pause()
+ {
+ lock (m_LockObject)
+ {
+ playbackState = PlaybackState.Paused;
+ }
+ }
+
+ ///
+ /// Gets the current position in bytes from the wave output device.
+ /// (n.b. this is not the same thing as the position within your reader
+ /// stream)
+ ///
+ /// Position in bytes
+ public long GetPosition()
+ {
+ if (playbackState != Wave.PlaybackState.Stopped)
+ {
+ var sbuf = secondaryBuffer;
+ if (sbuf != null)
+ {
+ uint currentPlayCursor, currentWriteCursor;
+ sbuf.GetCurrentPosition(out currentPlayCursor, out currentWriteCursor);
+ return currentPlayCursor + bytesPlayed;
+ }
+ }
+ return 0;
+ }
+
+ ///
+ /// Gets the current position from the wave output device.
+ ///
+ public TimeSpan PlaybackPosition
+ {
+ get
+ {
+ // bytes played in this stream
+ var pos = GetPosition();
+
+ // samples played in this stream
+ pos /= waveFormat.Channels * waveFormat.BitsPerSample / 8;
+
+ // ms played in this stream
+ return TimeSpan.FromMilliseconds(pos * 1000.0 / waveFormat.SampleRate);
+ }
+ }
+
+
+ ///
+ /// Initialise playback
+ ///
+ /// The waveprovider to be played
+ public void Init(IWaveProvider waveProvider)
+ {
+ this.waveStream = waveProvider;
+ this.waveFormat = waveProvider.WaveFormat;
+ }
+
+ private void InitializeDirectSound()
+ {
+ // Open DirectSound
+ lock (this.m_LockObject)
+ {
+ directSound = null;
+ DirectSoundCreate(ref device, out directSound, IntPtr.Zero);
+
+ if (directSound != null)
+ {
+ // Set Cooperative Level to PRIORITY (priority level can call the SetFormat and Compact methods)
+ directSound.SetCooperativeLevel(GetDesktopWindow(), DirectSoundCooperativeLevel.DSSCL_PRIORITY);
+
+ // -------------------------------------------------------------------------------------
+ // Create PrimaryBuffer
+ // -------------------------------------------------------------------------------------
+
+ // Fill BufferDescription for PrimaryBuffer
+ BufferDescription bufferDesc = new BufferDescription();
+ bufferDesc.dwSize = Marshal.SizeOf(bufferDesc);
+ bufferDesc.dwBufferBytes = 0;
+ bufferDesc.dwFlags = DirectSoundBufferCaps.DSBCAPS_PRIMARYBUFFER;
+ bufferDesc.dwReserved = 0;
+ bufferDesc.lpwfxFormat = IntPtr.Zero;
+ bufferDesc.guidAlgo = Guid.Empty;
+
+ object soundBufferObj;
+ // Create PrimaryBuffer
+ directSound.CreateSoundBuffer(bufferDesc, out soundBufferObj, IntPtr.Zero);
+ primarySoundBuffer = (IDirectSoundBuffer)soundBufferObj;
+
+ // Play & Loop on the PrimarySound Buffer
+ primarySoundBuffer.Play(0, 0, DirectSoundPlayFlags.DSBPLAY_LOOPING);
+
+ // -------------------------------------------------------------------------------------
+ // Create SecondaryBuffer
+ // -------------------------------------------------------------------------------------
+
+ // A frame of samples equals to Desired Latency
+ samplesFrameSize = MsToBytes(desiredLatency);
+
+ // Fill BufferDescription for SecondaryBuffer
+ BufferDescription bufferDesc2 = new BufferDescription();
+ bufferDesc2.dwSize = Marshal.SizeOf(bufferDesc2);
+ bufferDesc2.dwBufferBytes = (uint)(samplesFrameSize * 2);
+ bufferDesc2.dwFlags = DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2
+ | DirectSoundBufferCaps.DSBCAPS_CTRLPOSITIONNOTIFY
+ | DirectSoundBufferCaps.DSBCAPS_GLOBALFOCUS
+ | DirectSoundBufferCaps.DSBCAPS_CTRLVOLUME
+ | DirectSoundBufferCaps.DSBCAPS_STICKYFOCUS
+ | DirectSoundBufferCaps.DSBCAPS_GETCURRENTPOSITION2;
+ bufferDesc2.dwReserved = 0;
+ GCHandle handleOnWaveFormat = GCHandle.Alloc(waveFormat, GCHandleType.Pinned); // Ptr to waveFormat
+ bufferDesc2.lpwfxFormat = handleOnWaveFormat.AddrOfPinnedObject(); // set Ptr to waveFormat
+ bufferDesc2.guidAlgo = Guid.Empty;
+
+ // Create SecondaryBuffer
+ directSound.CreateSoundBuffer(bufferDesc2, out soundBufferObj, IntPtr.Zero);
+ secondaryBuffer = (IDirectSoundBuffer)soundBufferObj;
+ handleOnWaveFormat.Free();
+
+ // Get effective SecondaryBuffer size
+ BufferCaps dsbCaps = new BufferCaps();
+ dsbCaps.dwSize = Marshal.SizeOf(dsbCaps);
+ secondaryBuffer.GetCaps(dsbCaps);
+
+ nextSamplesWriteIndex = 0;
+ samplesTotalSize = dsbCaps.dwBufferBytes;
+ samples = new byte[samplesTotalSize];
+ System.Diagnostics.Debug.Assert(samplesTotalSize == (2 * samplesFrameSize), "Invalid SamplesTotalSize vs SamplesFrameSize");
+
+ // -------------------------------------------------------------------------------------
+ // Create double buffering notification.
+ // Use DirectSoundNotify at Position [0, 1/2] and Stop Position (0xFFFFFFFF)
+ // -------------------------------------------------------------------------------------
+ IDirectSoundNotify notify = (IDirectSoundNotify)soundBufferObj;
+
+ frameEventWaitHandle1 = new EventWaitHandle(false, EventResetMode.AutoReset);
+ frameEventWaitHandle2 = new EventWaitHandle(false, EventResetMode.AutoReset);
+ endEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
+
+ DirectSoundBufferPositionNotify[] notifies = new DirectSoundBufferPositionNotify[3];
+ notifies[0] = new DirectSoundBufferPositionNotify();
+ notifies[0].dwOffset = 0;
+ notifies[0].hEventNotify = frameEventWaitHandle1.SafeWaitHandle.DangerousGetHandle();
+
+ notifies[1] = new DirectSoundBufferPositionNotify();
+ notifies[1].dwOffset = (uint)samplesFrameSize;
+ notifies[1].hEventNotify = frameEventWaitHandle2.SafeWaitHandle.DangerousGetHandle();
+
+ notifies[2] = new DirectSoundBufferPositionNotify();
+ notifies[2].dwOffset = 0xFFFFFFFF;
+ notifies[2].hEventNotify = endEventWaitHandle.SafeWaitHandle.DangerousGetHandle();
+
+ notify.SetNotificationPositions(3, notifies);
+ }
+ }
+ }
+
+ ///
+ /// Current playback state
+ ///
+ ///
+ public PlaybackState PlaybackState
+ {
+ get { return playbackState; }
+ }
+
+ ///
+ /// The volume 1.0 is full scale
+ ///
+ ///
+ public float Volume
+ {
+ get
+ {
+ return 1.0f;
+ //return 1 + (secondaryBuffer.GetVolume()) / 10000.0f;
+ }
+ set
+ {
+ if (value != 1.0f)
+ {
+ throw new InvalidOperationException("Setting volume not supported on DirectSoundOut, adjust the volume on your WaveProvider instead");
+ }
+ //int intVol = (int)((value - 1) * 10000.0f);
+ //secondaryBuffer.SetVolume(intVol);
+ }
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ Stop();
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Determines whether the SecondaryBuffer is lost.
+ ///
+ ///
+ /// true if [is buffer lost]; otherwise, false .
+ ///
+ private bool IsBufferLost()
+ {
+ return (secondaryBuffer.GetStatus() & DirectSoundBufferStatus.DSBSTATUS_BUFFERLOST) != 0 ? true : false;
+ }
+
+ ///
+ /// Convert ms to bytes size according to WaveFormat
+ ///
+ /// The ms
+ /// number of byttes
+ private int MsToBytes(int ms)
+ {
+ int bytes = ms * (waveFormat.AverageBytesPerSecond / 1000);
+ bytes -= bytes % waveFormat.BlockAlign;
+ return bytes;
+ }
+
+ ///
+ /// Processes the samples in a separate thread.
+ ///
+ private void PlaybackThreadFunc()
+ {
+ // Used to determine if playback is halted
+ bool lPlaybackHalted = false;
+ bool firstBufferStarted = false;
+ bytesPlayed = 0;
+
+ Exception exception = null;
+ // Incase the thread is killed
+ try
+ {
+ InitializeDirectSound();
+ int lResult = 1;
+
+ if (PlaybackState == PlaybackState.Stopped)
+ {
+ secondaryBuffer.SetCurrentPosition(0);
+ nextSamplesWriteIndex = 0;
+ lResult = Feed(samplesTotalSize);
+ }
+
+ // Incase the previous Feed method returns 0
+ if (lResult > 0)
+ {
+ lock (m_LockObject)
+ {
+ playbackState = PlaybackState.Playing;
+ }
+
+ secondaryBuffer.Play(0, 0, DirectSoundPlayFlags.DSBPLAY_LOOPING);
+
+ var waitHandles = new WaitHandle[] { frameEventWaitHandle1, frameEventWaitHandle2, endEventWaitHandle };
+
+ bool lContinuePlayback = true;
+ while (PlaybackState != PlaybackState.Stopped && lContinuePlayback)
+ {
+ // Wait for signals on frameEventWaitHandle1 (Position 0), frameEventWaitHandle2 (Position 1/2)
+ int indexHandle = WaitHandle.WaitAny(waitHandles, 3 * desiredLatency, false);
+
+ // TimeOut is ok
+ if (indexHandle != WaitHandle.WaitTimeout)
+ {
+ // Buffer is Stopped
+ if (indexHandle == 2)
+ {
+ // (Gee) - Not sure whether to stop playback in this case or not!
+ StopPlayback();
+ lPlaybackHalted = true;
+ lContinuePlayback = false;
+ }
+ else
+ {
+ if (indexHandle == 0)
+ {
+ // we're at the beginning of the buffer...
+ if (firstBufferStarted)
+ {
+ // because this notification is based on the *playback" cursor, this should be reasonably accurate
+ bytesPlayed += samplesFrameSize * 2;
+ }
+ }
+ else
+ {
+ firstBufferStarted = true;
+ }
+
+ indexHandle = (indexHandle == 0) ? 1 : 0;
+ nextSamplesWriteIndex = indexHandle * samplesFrameSize;
+
+ // Only carry on playing if we can!
+ if (Feed(samplesFrameSize) == 0)
+ {
+ StopPlayback();
+ lPlaybackHalted = true;
+ lContinuePlayback = false;
+ }
+ }
+ }
+ else
+ {
+ // Timed out!
+ StopPlayback();
+ lPlaybackHalted = true;
+ lContinuePlayback = false;
+ // report this as an error in the Playback Stopped
+ // seems to happen when device is unplugged
+ throw new Exception("DirectSound buffer timeout");
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // Do nothing (except report error)
+ Debug.WriteLine(e.ToString());
+ exception = e;
+ }
+ finally
+ {
+ if (!lPlaybackHalted)
+ {
+ try
+ {
+ StopPlayback();
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.ToString());
+ // don't overwrite the original reason we exited the playback loop
+ if (exception == null) exception = e;
+ }
+ }
+
+ lock (m_LockObject)
+ {
+ playbackState = PlaybackState.Stopped;
+ }
+
+ bytesPlayed = 0;
+
+ // Fire playback stopped event
+ RaisePlaybackStopped(exception);
+ }
+ }
+
+ private void RaisePlaybackStopped(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+
+
+ ///
+ /// Stop playback
+ ///
+ private void StopPlayback()
+ {
+ lock (this.m_LockObject)
+ {
+ if (secondaryBuffer != null)
+ {
+ secondaryBuffer.Stop();
+ secondaryBuffer = null;
+ }
+ if (primarySoundBuffer != null)
+ {
+ primarySoundBuffer.Stop();
+ primarySoundBuffer = null;
+ }
+ }
+ }
+
+
+ ///
+ /// Feeds the SecondaryBuffer with the WaveStream
+ ///
+ /// number of bytes to feed
+ private int Feed(int bytesToCopy)
+ {
+ int bytesRead = bytesToCopy;
+
+ // Restore the buffer if lost
+ if (IsBufferLost())
+ {
+ secondaryBuffer.Restore();
+ }
+
+ // Clear the bufferSamples if in Paused
+ if (playbackState == PlaybackState.Paused)
+ {
+ Array.Clear(samples, 0, samples.Length);
+ }
+ else
+ {
+ // Read data from stream (Should this be inserted between the lock / unlock?)
+ bytesRead = waveStream.Read(samples, 0, bytesToCopy);
+
+ if (bytesRead == 0)
+ {
+ Array.Clear(samples, 0, samples.Length);
+ return 0;
+ }
+ }
+
+ // Lock a portion of the SecondaryBuffer (starting from 0 or 1/2 the buffer)
+ IntPtr wavBuffer1;
+ int nbSamples1;
+ IntPtr wavBuffer2;
+ int nbSamples2;
+ secondaryBuffer.Lock(nextSamplesWriteIndex, (uint)bytesRead, // (uint)bytesToCopy,
+ out wavBuffer1, out nbSamples1,
+ out wavBuffer2, out nbSamples2,
+ DirectSoundBufferLockFlag.None);
+
+ // Copy back to the SecondaryBuffer
+ if (wavBuffer1 != IntPtr.Zero)
+ {
+ Marshal.Copy(samples, 0, wavBuffer1, nbSamples1);
+ if (wavBuffer2 != IntPtr.Zero)
+ {
+ Marshal.Copy(samples, 0, wavBuffer1, nbSamples1);
+ }
+ }
+
+ // Unlock the SecondaryBuffer
+ secondaryBuffer.Unlock(wavBuffer1, nbSamples1, wavBuffer2, nbSamples2);
+
+ return bytesRead;
+ }
+
+
+ //----------------------------------------------------------------------------------------------
+ // Minimal Native DirectSound COM interop interfaces
+ //----------------------------------------------------------------------------------------------
+ #region Native DirectSound COM Interface
+
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
+ internal class BufferDescription
+ {
+ public int dwSize;
+ [MarshalAs(UnmanagedType.U4)]
+ public DirectSoundBufferCaps dwFlags;
+ public uint dwBufferBytes;
+ public int dwReserved;
+ public IntPtr lpwfxFormat;
+ public Guid guidAlgo;
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 2)]
+ internal class BufferCaps
+ {
+ public int dwSize;
+ public int dwFlags;
+ public int dwBufferBytes;
+ public int dwUnlockTransferRate;
+ public int dwPlayCpuOverhead;
+ }
+
+ internal enum DirectSoundCooperativeLevel : uint
+ {
+ DSSCL_NORMAL = 0x00000001,
+ DSSCL_PRIORITY = 0x00000002,
+ DSSCL_EXCLUSIVE = 0x00000003,
+ DSSCL_WRITEPRIMARY = 0x00000004
+ }
+
+ [FlagsAttribute]
+ internal enum DirectSoundPlayFlags : uint
+ {
+ DSBPLAY_LOOPING = 0x00000001,
+ DSBPLAY_LOCHARDWARE = 0x00000002,
+ DSBPLAY_LOCSOFTWARE = 0x00000004,
+ DSBPLAY_TERMINATEBY_TIME = 0x00000008,
+ DSBPLAY_TERMINATEBY_DISTANCE = 0x000000010,
+ DSBPLAY_TERMINATEBY_PRIORITY = 0x000000020
+ }
+
+ internal enum DirectSoundBufferLockFlag : uint
+ {
+ None = 0,
+ FromWriteCursor = 0x00000001,
+ EntireBuffer = 0x00000002
+ }
+
+ [FlagsAttribute]
+ internal enum DirectSoundBufferStatus : uint
+ {
+ DSBSTATUS_PLAYING = 0x00000001,
+ DSBSTATUS_BUFFERLOST = 0x00000002,
+ DSBSTATUS_LOOPING = 0x00000004,
+ DSBSTATUS_LOCHARDWARE = 0x00000008,
+ DSBSTATUS_LOCSOFTWARE = 0x00000010,
+ DSBSTATUS_TERMINATED = 0x00000020
+ }
+
+ [FlagsAttribute]
+ internal enum DirectSoundBufferCaps : uint
+ {
+ DSBCAPS_PRIMARYBUFFER = 0x00000001,
+ DSBCAPS_STATIC = 0x00000002,
+ DSBCAPS_LOCHARDWARE = 0x00000004,
+ DSBCAPS_LOCSOFTWARE = 0x00000008,
+ DSBCAPS_CTRL3D = 0x00000010,
+ DSBCAPS_CTRLFREQUENCY = 0x00000020,
+ DSBCAPS_CTRLPAN = 0x00000040,
+ DSBCAPS_CTRLVOLUME = 0x00000080,
+ DSBCAPS_CTRLPOSITIONNOTIFY = 0x00000100,
+ DSBCAPS_CTRLFX = 0x00000200,
+ DSBCAPS_STICKYFOCUS = 0x00004000,
+ DSBCAPS_GLOBALFOCUS = 0x00008000,
+ DSBCAPS_GETCURRENTPOSITION2 = 0x00010000,
+ DSBCAPS_MUTE3DATMAXDISTANCE = 0x00020000,
+ DSBCAPS_LOCDEFER = 0x00040000
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct DirectSoundBufferPositionNotify
+ {
+ public UInt32 dwOffset;
+ public IntPtr hEventNotify;
+ }
+
+ ///
+ /// IDirectSound interface
+ ///
+ [ComImport,
+ Guid("279AFA83-4981-11CE-A521-0020AF0BE560"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
+ SuppressUnmanagedCodeSecurity]
+ internal interface IDirectSound
+ {
+ //STDMETHOD(CreateSoundBuffer) (THIS_ LPCDSBUFFERDESC pcDSBufferDesc, LPDIRECTSOUNDBUFFER *ppDSBuffer, LPUNKNOWN pUnkOuter) PURE;
+ void CreateSoundBuffer([In] BufferDescription desc, [Out, MarshalAs(UnmanagedType.Interface)] out object dsDSoundBuffer, IntPtr pUnkOuter);
+ //STDMETHOD(GetCaps) (THIS_ LPDSCAPS pDSCaps) PURE;
+ void GetCaps(IntPtr caps);
+ //STDMETHOD(DuplicateSoundBuffer) (THIS_ LPDIRECTSOUNDBUFFER pDSBufferOriginal, LPDIRECTSOUNDBUFFER *ppDSBufferDuplicate) PURE;
+ void DuplicateSoundBuffer([In, MarshalAs(UnmanagedType.Interface)] IDirectSoundBuffer bufferOriginal, [In, MarshalAs(UnmanagedType.Interface)] IDirectSoundBuffer bufferDuplicate);
+ //STDMETHOD(SetCooperativeLevel) (THIS_ HWND hwnd, DWORD dwLevel) PURE;
+ void SetCooperativeLevel(IntPtr HWND, [In, MarshalAs(UnmanagedType.U4)] DirectSoundCooperativeLevel dwLevel);
+ //STDMETHOD(Compact) (THIS) PURE;
+ void Compact();
+ //STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD pdwSpeakerConfig) PURE;
+ void GetSpeakerConfig(IntPtr pdwSpeakerConfig);
+ //STDMETHOD(SetSpeakerConfig) (THIS_ DWORD dwSpeakerConfig) PURE;
+ void SetSpeakerConfig(uint pdwSpeakerConfig);
+ //STDMETHOD(Initialize) (THIS_ LPCGUID pcGuidDevice) PURE;
+ void Initialize([In, MarshalAs(UnmanagedType.LPStruct)] Guid guid);
+ }
+
+ ///
+ /// IDirectSoundBuffer interface
+ ///
+ [ComImport,
+ Guid("279AFA85-4981-11CE-A521-0020AF0BE560"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
+ SuppressUnmanagedCodeSecurity]
+ internal interface IDirectSoundBuffer
+ {
+ // STDMETHOD(GetCaps) (THIS_ LPDSBCAPS pDSBufferCaps) PURE;
+ void GetCaps([MarshalAs(UnmanagedType.LPStruct)] BufferCaps pBufferCaps);
+ // STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD pdwCurrentPlayCursor, LPDWORD pdwCurrentWriteCursor) PURE;
+ void GetCurrentPosition([Out] out uint currentPlayCursor, [Out] out uint currentWriteCursor);
+ // STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX pwfxFormat, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten) PURE;
+ void GetFormat();
+ // STDMETHOD(GetVolume) (THIS_ LPLONG plVolume) PURE;
+ [return: MarshalAs(UnmanagedType.I4)]
+ int GetVolume();
+ // STDMETHOD(GetPan) (THIS_ LPLONG plPan) PURE;
+ void GetPan([Out] out uint pan);
+ // STDMETHOD(GetFrequency) (THIS_ LPDWORD pdwFrequency) PURE;
+ [return: MarshalAs(UnmanagedType.I4)]
+ int GetFrequency();
+ // STDMETHOD(GetStatus) (THIS_ LPDWORD pdwStatus) PURE;
+ [return: MarshalAs(UnmanagedType.U4)]
+ DirectSoundBufferStatus GetStatus();
+ // STDMETHOD(Initialize) (THIS_ LPDIRECTSOUND pDirectSound, LPCDSBUFFERDESC pcDSBufferDesc) PURE;
+ void Initialize([In, MarshalAs(UnmanagedType.Interface)] IDirectSound directSound, [In] BufferDescription desc);
+ // STDMETHOD(Lock) (THIS_ DWORD dwOffset, DWORD dwBytes, LPVOID *ppvAudioPtr1, LPDWORD pdwAudioBytes1,
+ // LPVOID *ppvAudioPtr2, LPDWORD pdwAudioBytes2, DWORD dwFlags) PURE;
+ void Lock(int dwOffset, uint dwBytes, [Out] out IntPtr audioPtr1, [Out] out int audioBytes1, [Out] out IntPtr audioPtr2, [Out] out int audioBytes2, [MarshalAs(UnmanagedType.U4)] DirectSoundBufferLockFlag dwFlags);
+ // STDMETHOD(Play) (THIS_ DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) PURE;
+ void Play(uint dwReserved1, uint dwPriority, [In, MarshalAs(UnmanagedType.U4)] DirectSoundPlayFlags dwFlags);
+ // STDMETHOD(SetCurrentPosition) (THIS_ DWORD dwNewPosition) PURE;
+ void SetCurrentPosition(uint dwNewPosition);
+ // STDMETHOD(SetFormat) (THIS_ LPCWAVEFORMATEX pcfxFormat) PURE;
+ void SetFormat([In] WaveFormat pcfxFormat);
+ // STDMETHOD(SetVolume) (THIS_ LONG lVolume) PURE;
+ void SetVolume(int volume);
+ // STDMETHOD(SetPan) (THIS_ LONG lPan) PURE;
+ void SetPan(uint pan);
+ // STDMETHOD(SetFrequency) (THIS_ DWORD dwFrequency) PURE;
+ void SetFrequency(uint frequency);
+ // STDMETHOD(Stop) (THIS) PURE;
+ void Stop();
+ // STDMETHOD(Unlock) (THIS_ LPVOID pvAudioPtr1, DWORD dwAudioBytes1, LPVOID pvAudioPtr2, DWORD dwAudioBytes2) PURE;
+ void Unlock(IntPtr pvAudioPtr1, int dwAudioBytes1, IntPtr pvAudioPtr2, int dwAudioBytes2);
+ // STDMETHOD(Restore) (THIS) PURE;
+ void Restore();
+ }
+
+ ///
+ /// IDirectSoundNotify interface
+ ///
+ [ComImport,
+ Guid("b0210783-89cd-11d0-af08-00a0c925cd16"),
+ InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
+ SuppressUnmanagedCodeSecurity]
+ internal interface IDirectSoundNotify
+ {
+ void SetNotificationPositions(UInt32 dwPositionNotifies, [In, MarshalAs(UnmanagedType.LPArray)] DirectSoundBufferPositionNotify[] pcPositionNotifies);
+ }
+
+ ///
+ /// Instanciate DirectSound from the DLL
+ ///
+ /// The GUID.
+ /// The direct sound.
+ /// The p unk outer.
+ [DllImport("dsound.dll", EntryPoint = "DirectSoundCreate", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
+ static extern void DirectSoundCreate(ref Guid GUID, [Out, MarshalAs(UnmanagedType.Interface)] out IDirectSound directSound, IntPtr pUnkOuter);
+
+
+ ///
+ /// DirectSound default playback device GUID
+ ///
+ public static readonly Guid DSDEVID_DefaultPlayback = new Guid("DEF00000-9C6D-47ED-AAF1-4DDA8F2B5C03");
+
+ ///
+ /// DirectSound default capture device GUID
+ ///
+ public static readonly Guid DSDEVID_DefaultCapture = new Guid("DEF00001-9C6D-47ED-AAF1-4DDA8F2B5C03");
+
+ ///
+ /// DirectSound default device for voice playback
+ ///
+ public static readonly Guid DSDEVID_DefaultVoicePlayback = new Guid("DEF00002-9C6D-47ED-AAF1-4DDA8F2B5C03");
+
+ ///
+ /// DirectSound default device for voice capture
+ ///
+ public static readonly Guid DSDEVID_DefaultVoiceCapture = new Guid("DEF00003-9C6D-47ED-AAF1-4DDA8F2B5C03");
+
+ ///
+ /// The DSEnumCallback function is an application-defined callback function that enumerates the DirectSound drivers.
+ /// The system calls this function in response to the application's call to the DirectSoundEnumerate or DirectSoundCaptureEnumerate function.
+ ///
+ /// Address of the GUID that identifies the device being enumerated, or NULL for the primary device. This value can be passed to the DirectSoundCreate8 or DirectSoundCaptureCreate8 function to create a device object for that driver.
+ /// Address of a null-terminated string that provides a textual description of the DirectSound device.
+ /// Address of a null-terminated string that specifies the module name of the DirectSound driver corresponding to this device.
+ /// Address of application-defined data. This is the pointer passed to DirectSoundEnumerate or DirectSoundCaptureEnumerate as the lpContext parameter.
+ /// Returns TRUE to continue enumerating drivers, or FALSE to stop.
+ delegate bool DSEnumCallback(IntPtr lpGuid, IntPtr lpcstrDescription, IntPtr lpcstrModule, IntPtr lpContext);
+
+ ///
+ /// The DirectSoundEnumerate function enumerates the DirectSound drivers installed in the system.
+ ///
+ /// callback function
+ /// User context
+ [DllImport("dsound.dll", EntryPoint = "DirectSoundEnumerateA", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
+ static extern void DirectSoundEnumerate(DSEnumCallback lpDSEnumCallback, IntPtr lpContext);
+
+ ///
+ /// Gets the HANDLE of the desktop window.
+ ///
+ /// HANDLE of the Desktop window
+ [DllImport("user32.dll")]
+ private static extern IntPtr GetDesktopWindow();
+ #endregion
+ }
+
+ ///
+ /// Class for enumerating DirectSound devices
+ ///
+ public class DirectSoundDeviceInfo
+ {
+ ///
+ /// The device identifier
+ ///
+ public Guid Guid { get; set; }
+ ///
+ /// Device description
+ ///
+ public string Description { get; set; }
+ ///
+ /// Device module name
+ ///
+ public string ModuleName { get; set; }
+ }
+
+}
diff --git a/NAudio/Wave/WaveOutputs/IWaveBuffer.cs b/NAudio/Wave/WaveOutputs/IWaveBuffer.cs
new file mode 100644
index 00000000..2842c33a
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/IWaveBuffer.cs
@@ -0,0 +1,70 @@
+namespace NAudio.Wave
+{
+ ///
+ /// IWaveBuffer interface use to store wave datas.
+ /// Data can be manipulated with arrays ( , ,
+ /// , ) that are pointing to the same memory buffer.
+ /// This is a requirement for all subclasses.
+ ///
+ /// Use the associated Count property based on the type of buffer to get the number of data in the
+ /// buffer.
+ ///
+ /// for the standard implementation using C# unions.
+ ///
+ public interface IWaveBuffer
+ {
+ ///
+ /// Gets the byte buffer.
+ ///
+ /// The byte buffer.
+ byte[] ByteBuffer { get; }
+
+ ///
+ /// Gets the float buffer.
+ ///
+ /// The float buffer.
+ float[] FloatBuffer { get; }
+
+ ///
+ /// Gets the short buffer.
+ ///
+ /// The short buffer.
+ short[] ShortBuffer { get; }
+
+ ///
+ /// Gets the int buffer.
+ ///
+ /// The int buffer.
+ int[] IntBuffer { get; }
+
+ ///
+ /// Gets the max size in bytes of the byte buffer..
+ ///
+ /// Maximum number of bytes in the buffer.
+ int MaxSize { get; }
+
+ ///
+ /// Gets the byte buffer count.
+ ///
+ /// The byte buffer count.
+ int ByteBufferCount { get; }
+
+ ///
+ /// Gets the float buffer count.
+ ///
+ /// The float buffer count.
+ int FloatBufferCount { get;}
+
+ ///
+ /// Gets the short buffer count.
+ ///
+ /// The short buffer count.
+ int ShortBufferCount { get; }
+
+ ///
+ /// Gets the int buffer count.
+ ///
+ /// The int buffer count.
+ int IntBufferCount { get; }
+ }
+}
\ No newline at end of file
diff --git a/NAudio/Wave/WaveOutputs/IWavePlayer.cs b/NAudio/Wave/WaveOutputs/IWavePlayer.cs
new file mode 100644
index 00000000..16e0aa2c
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/IWavePlayer.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Represents the interface to a device that can play a WaveFile
+ ///
+ public interface IWavePlayer : IDisposable
+ {
+ ///
+ /// Begin playback
+ ///
+ void Play();
+
+ ///
+ /// Stop playback
+ ///
+ void Stop();
+
+ ///
+ /// Pause Playback
+ ///
+ void Pause();
+
+ ///
+ /// Initialise playback
+ ///
+ /// The waveprovider to be played
+ void Init(IWaveProvider waveProvider);
+
+ ///
+ /// Current playback state
+ ///
+ PlaybackState PlaybackState { get; }
+
+ ///
+ /// The volume 1.0 is full scale
+ ///
+ [Obsolete("Not intending to keep supporting this going forward: set the volume on your input WaveProvider instead")]
+ float Volume { get; set; }
+
+ ///
+ /// Indicates that playback has gone into a stopped state due to
+ /// reaching the end of the input stream or an error has been encountered during playback
+ ///
+ event EventHandler PlaybackStopped;
+ }
+
+ ///
+ /// Interface for IWavePlayers that can report position
+ ///
+ public interface IWavePosition
+ {
+ ///
+ /// Position (in terms of bytes played - does not necessarily)
+ ///
+ /// Position in bytes
+ long GetPosition();
+
+ ///
+ /// Gets a instance indicating the format the hardware is using.
+ ///
+ WaveFormat OutputWaveFormat { get; }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/IWaveProvider.cs b/NAudio/Wave/WaveOutputs/IWaveProvider.cs
new file mode 100644
index 00000000..e84c9147
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/IWaveProvider.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Generic interface for all WaveProviders.
+ ///
+ public interface IWaveProvider
+ {
+ ///
+ /// Gets the WaveFormat of this WaveProvider.
+ ///
+ /// The wave format.
+ WaveFormat WaveFormat { get; }
+
+ ///
+ /// Fill the specified buffer with wave data.
+ ///
+ /// The buffer to fill of wave data.
+ /// Offset into buffer
+ /// The number of bytes to read
+ /// the number of bytes written to the buffer.
+ int Read(byte[] buffer, int offset, int count);
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/IWaveProviderFloat.cs b/NAudio/Wave/WaveOutputs/IWaveProviderFloat.cs
new file mode 100644
index 00000000..3dfe16a8
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/IWaveProviderFloat.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Like IWaveProvider, but makes it much simpler to put together a 32 bit floating
+ /// point mixing engine
+ ///
+ public interface ISampleProvider
+ {
+ ///
+ /// Gets the WaveFormat of this Sample Provider.
+ ///
+ /// The wave format.
+ WaveFormat WaveFormat { get; }
+
+ ///
+ /// Fill the specified buffer with 32 bit floating point samples
+ ///
+ /// The buffer to fill with samples.
+ /// Offset into buffer
+ /// The number of samples to read
+ /// the number of samples written to the buffer.
+ int Read(float[] buffer, int offset, int count);
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/MediaFoundationEncoder.cs b/NAudio/Wave/WaveOutputs/MediaFoundationEncoder.cs
new file mode 100644
index 00000000..3cb36a8a
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/MediaFoundationEncoder.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using NAudio.MediaFoundation;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Media Foundation Encoder class allows you to use Media Foundation to encode an IWaveProvider
+ /// to any supported encoding format
+ ///
+ public class MediaFoundationEncoder : IDisposable
+ {
+ ///
+ /// Queries the available bitrates for a given encoding output type, sample rate and number of channels
+ ///
+ /// Audio subtype - a value from the AudioSubtypes class
+ /// The sample rate of the PCM to encode
+ /// The number of channels of the PCM to encode
+ /// An array of available bitrates in average bits per second
+ public static int[] GetEncodeBitrates(Guid audioSubtype, int sampleRate, int channels)
+ {
+ return GetOutputMediaTypes(audioSubtype)
+ .Where(mt => mt.SampleRate == sampleRate && mt.ChannelCount == channels)
+ .Select(mt => mt.AverageBytesPerSecond*8)
+ .Distinct()
+ .OrderBy(br => br)
+ .ToArray();
+ }
+
+ ///
+ /// Gets all the available media types for a particular
+ ///
+ /// Audio subtype - a value from the AudioSubtypes class
+ /// An array of available media types that can be encoded with this subtype
+ public static MediaType[] GetOutputMediaTypes(Guid audioSubtype)
+ {
+ IMFCollection availableTypes;
+ try
+ {
+ MediaFoundationInterop.MFTranscodeGetAudioOutputAvailableTypes(
+ audioSubtype, _MFT_ENUM_FLAG.MFT_ENUM_FLAG_ALL, null, out availableTypes);
+ }
+ catch (COMException c)
+ {
+ if (c.GetHResult() == MediaFoundationErrors.MF_E_NOT_FOUND)
+ {
+ // Don't worry if we didn't find any - just means no encoder available for this type
+ return new MediaType[0];
+ }
+ else
+ {
+ throw;
+ }
+ }
+ int count;
+ availableTypes.GetElementCount(out count);
+ var mediaTypes = new List(count);
+ for (int n = 0; n < count; n++)
+ {
+ object mediaTypeObject;
+ availableTypes.GetElement(n, out mediaTypeObject);
+ var mediaType = (IMFMediaType)mediaTypeObject;
+ mediaTypes.Add(new MediaType(mediaType));
+ }
+ Marshal.ReleaseComObject(availableTypes);
+ return mediaTypes.ToArray();
+ }
+
+ ///
+ /// Helper function to simplify encoding Window Media Audio
+ /// Should be supported on Vista and above (not tested)
+ ///
+ /// Input provider, must be PCM
+ /// Output file path, should end with .wma
+ /// Desired bitrate. Use GetEncodeBitrates to find the possibilities for your input type
+ public static void EncodeToWma(IWaveProvider inputProvider, string outputFile, int desiredBitRate = 192000)
+ {
+ var mediaType = SelectMediaType(AudioSubtypes.MFAudioFormat_WMAudioV8, inputProvider.WaveFormat, desiredBitRate);
+ if (mediaType == null) throw new InvalidOperationException("No suitable WMA encoders available");
+ using (var encoder = new MediaFoundationEncoder(mediaType))
+ {
+ encoder.Encode(outputFile, inputProvider);
+ }
+ }
+
+ ///
+ /// Helper function to simplify encoding to MP3
+ /// By default, will only be available on Windows 8 and above
+ ///
+ /// Input provider, must be PCM
+ /// Output file path, should end with .mp3
+ /// Desired bitrate. Use GetEncodeBitrates to find the possibilities for your input type
+ public static void EncodeToMp3(IWaveProvider inputProvider, string outputFile, int desiredBitRate = 192000)
+ {
+ var mediaType = SelectMediaType(AudioSubtypes.MFAudioFormat_MP3, inputProvider.WaveFormat, desiredBitRate);
+ if (mediaType == null) throw new InvalidOperationException("No suitable MP3 encoders available");
+ using (var encoder = new MediaFoundationEncoder(mediaType))
+ {
+ encoder.Encode(outputFile, inputProvider);
+ }
+ }
+
+ ///
+ /// Helper function to simplify encoding to AAC
+ /// By default, will only be available on Windows 7 and above
+ ///
+ /// Input provider, must be PCM
+ /// Output file path, should end with .mp4 (or .aac on Windows 8)
+ /// Desired bitrate. Use GetEncodeBitrates to find the possibilities for your input type
+ public static void EncodeToAac(IWaveProvider inputProvider, string outputFile, int desiredBitRate = 192000)
+ {
+ // Information on configuring an AAC media type can be found here:
+ // http://msdn.microsoft.com/en-gb/library/windows/desktop/dd742785%28v=vs.85%29.aspx
+ var mediaType = SelectMediaType(AudioSubtypes.MFAudioFormat_AAC, inputProvider.WaveFormat, desiredBitRate);
+ if (mediaType == null) throw new InvalidOperationException("No suitable AAC encoders available");
+ using (var encoder = new MediaFoundationEncoder(mediaType))
+ {
+ // should AAC container have ADTS, or is that just for ADTS?
+ // http://www.hydrogenaudio.org/forums/index.php?showtopic=97442
+ encoder.Encode(outputFile, inputProvider);
+ }
+ }
+
+ ///
+ /// Tries to find the encoding media type with the closest bitrate to that specified
+ ///
+ /// Audio subtype, a value from AudioSubtypes
+ /// Your encoder input format (used to check sample rate and channel count)
+ /// Your desired bitrate
+ /// The closest media type, or null if none available
+ public static MediaType SelectMediaType(Guid audioSubtype, WaveFormat inputFormat, int desiredBitRate)
+ {
+ return GetOutputMediaTypes(audioSubtype)
+ .Where(mt => mt.SampleRate == inputFormat.SampleRate && mt.ChannelCount == inputFormat.Channels)
+ .Select(mt => new { MediaType = mt, Delta = Math.Abs(desiredBitRate - mt.AverageBytesPerSecond * 8) } )
+ .OrderBy(mt => mt.Delta)
+ .Select(mt => mt.MediaType)
+ .FirstOrDefault();
+ }
+
+ private readonly MediaType outputMediaType;
+ private bool disposed;
+
+ ///
+ /// Creates a new encoder that encodes to the specified output media type
+ ///
+ /// Desired output media type
+ public MediaFoundationEncoder(MediaType outputMediaType)
+ {
+ if (outputMediaType == null) throw new ArgumentNullException("outputMediaType");
+ this.outputMediaType = outputMediaType;
+ }
+
+ ///
+ /// Encodes a file
+ ///
+ /// Output filename (container type is deduced from the filename)
+ /// Input provider (should be PCM, some encoders will also allow IEEE float)
+ public void Encode(string outputFile, IWaveProvider inputProvider)
+ {
+ if (inputProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm && inputProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ {
+ throw new ArgumentException("Encode input format must be PCM or IEEE float");
+ }
+
+ var inputMediaType = new MediaType(inputProvider.WaveFormat);
+
+ var writer = CreateSinkWriter(outputFile);
+ try
+ {
+ int streamIndex;
+ writer.AddStream(outputMediaType.MediaFoundationObject, out streamIndex);
+
+ // n.b. can get 0xC00D36B4 - MF_E_INVALIDMEDIATYPE here
+ writer.SetInputMediaType(streamIndex, inputMediaType.MediaFoundationObject, null);
+
+ PerformEncode(writer, streamIndex, inputProvider);
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(writer);
+ Marshal.ReleaseComObject(inputMediaType.MediaFoundationObject);
+ }
+ }
+
+ private static IMFSinkWriter CreateSinkWriter(string outputFile)
+ {
+ // n.b. could try specifying the container type using attributes, but I think
+ // it does a decent job of working it out from the file extension
+ // n.b. AAC encode on Win 8 can have AAC extension, but use MP4 in win 7
+ // http://msdn.microsoft.com/en-gb/library/windows/desktop/dd389284%28v=vs.85%29.aspx
+ IMFSinkWriter writer;
+ var attributes = MediaFoundationApi.CreateAttributes(1);
+ attributes.SetUINT32(MediaFoundationAttributes.MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, 1);
+ try
+ {
+ MediaFoundationInterop.MFCreateSinkWriterFromURL(outputFile, null, attributes, out writer);
+ }
+ catch (COMException e)
+ {
+ if (e.GetHResult() == MediaFoundationErrors.MF_E_NOT_FOUND)
+ {
+ throw new ArgumentException("Was not able to create a sink writer for this file extension");
+ }
+ throw;
+ }
+ finally
+ {
+ Marshal.ReleaseComObject(attributes);
+ }
+ return writer;
+ }
+
+ private void PerformEncode(IMFSinkWriter writer, int streamIndex, IWaveProvider inputProvider)
+ {
+ int maxLength = inputProvider.WaveFormat.AverageBytesPerSecond * 4;
+ var managedBuffer = new byte[maxLength];
+
+ writer.BeginWriting();
+
+ long position = 0;
+ long duration = 0;
+ do
+ {
+ duration = ConvertOneBuffer(writer, streamIndex, inputProvider, position, managedBuffer);
+ position += duration;
+ } while (duration > 0);
+
+ writer.DoFinalize();
+ }
+
+ private static long BytesToNsPosition(int bytes, WaveFormat waveFormat)
+ {
+ long nsPosition = (10000000L * bytes) / waveFormat.AverageBytesPerSecond;
+ return nsPosition;
+ }
+
+ private long ConvertOneBuffer(IMFSinkWriter writer, int streamIndex, IWaveProvider inputProvider, long position, byte[] managedBuffer)
+ {
+ long durationConverted = 0;
+ int maxLength;
+ IMFMediaBuffer buffer = MediaFoundationApi.CreateMemoryBuffer(managedBuffer.Length);
+ buffer.GetMaxLength(out maxLength);
+
+ IMFSample sample = MediaFoundationApi.CreateSample();
+ sample.AddBuffer(buffer);
+
+ IntPtr ptr;
+ int currentLength;
+ buffer.Lock(out ptr, out maxLength, out currentLength);
+ int read = inputProvider.Read(managedBuffer, 0, maxLength);
+ if (read > 0)
+ {
+ durationConverted = BytesToNsPosition(read, inputProvider.WaveFormat);
+ Marshal.Copy(managedBuffer, 0, ptr, read);
+ buffer.SetCurrentLength(read);
+ buffer.Unlock();
+ sample.SetSampleTime(position);
+ sample.SetSampleDuration(durationConverted);
+ writer.WriteSample(streamIndex, sample);
+ //writer.Flush(streamIndex);
+ }
+ else
+ {
+ buffer.Unlock();
+ }
+
+ Marshal.ReleaseComObject(sample);
+ Marshal.ReleaseComObject(buffer);
+ return durationConverted;
+ }
+
+ ///
+ /// Disposes this instance
+ ///
+ ///
+ protected void Dispose(bool disposing)
+ {
+ Marshal.ReleaseComObject(outputMediaType.MediaFoundationObject);
+ }
+
+ ///
+ /// Disposes this instance
+ ///
+ public void Dispose()
+ {
+ if (!disposed)
+ {
+ disposed = true;
+ Dispose(true);
+ }
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Finalizer
+ ///
+ ~MediaFoundationEncoder()
+ {
+ Dispose(false);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/PlaybackState.cs b/NAudio/Wave/WaveOutputs/PlaybackState.cs
new file mode 100644
index 00000000..c7871183
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/PlaybackState.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Playback State
+ ///
+ public enum PlaybackState
+ {
+ ///
+ /// Stopped
+ ///
+ Stopped,
+ ///
+ /// Playing
+ ///
+ Playing,
+ ///
+ /// Paused
+ ///
+ Paused
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/StoppedEventArgs.cs b/NAudio/Wave/WaveOutputs/StoppedEventArgs.cs
new file mode 100644
index 00000000..41b1d06e
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/StoppedEventArgs.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Stopped Event Args
+ ///
+ public class StoppedEventArgs : EventArgs
+ {
+ private readonly Exception exception;
+
+ ///
+ /// Initializes a new instance of StoppedEventArgs
+ ///
+ /// An exception to report (null if no exception)
+ public StoppedEventArgs(Exception exception = null)
+ {
+ this.exception = exception;
+ }
+
+ ///
+ /// An exception. Will be null if the playback or record operation stopped
+ ///
+ public Exception Exception { get { return exception; } }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/WasapiOut.cs b/NAudio/Wave/WaveOutputs/WasapiOut.cs
new file mode 100644
index 00000000..73db5df0
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/WasapiOut.cs
@@ -0,0 +1,431 @@
+using System;
+using NAudio.CoreAudioApi;
+using System.Threading;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Support for playback using Wasapi
+ ///
+ public class WasapiOut : IWavePlayer, IWavePosition
+ {
+ private AudioClient audioClient;
+ private readonly MMDevice mmDevice;
+ private readonly AudioClientShareMode shareMode;
+ private AudioRenderClient renderClient;
+ private IWaveProvider sourceProvider;
+ private int latencyMilliseconds;
+ private int bufferFrameCount;
+ private int bytesPerFrame;
+ private readonly bool isUsingEventSync;
+ private EventWaitHandle frameEventWaitHandle;
+ private byte[] readBuffer;
+ private volatile PlaybackState playbackState;
+ private Thread playThread;
+ private WaveFormat outputFormat;
+ private bool dmoResamplerNeeded;
+ private readonly SynchronizationContext syncContext;
+
+ ///
+ /// Playback Stopped
+ ///
+ public event EventHandler PlaybackStopped;
+
+ ///
+ /// WASAPI Out using default audio endpoint
+ ///
+ /// ShareMode - shared or exclusive
+ /// Desired latency in milliseconds
+ public WasapiOut(AudioClientShareMode shareMode, int latency) :
+ this(GetDefaultAudioEndpoint(), shareMode, true, latency)
+ {
+
+ }
+
+ ///
+ /// WASAPI Out using default audio endpoint
+ ///
+ /// ShareMode - shared or exclusive
+ /// true if sync is done with event. false use sleep.
+ /// Desired latency in milliseconds
+ public WasapiOut(AudioClientShareMode shareMode, bool useEventSync, int latency) :
+ this(GetDefaultAudioEndpoint(), shareMode, useEventSync, latency)
+ {
+
+ }
+
+ ///
+ /// Creates a new WASAPI Output
+ ///
+ /// Device to use
+ ///
+ /// true if sync is done with event. false use sleep.
+ ///
+ public WasapiOut(MMDevice device, AudioClientShareMode shareMode, bool useEventSync, int latency)
+ {
+ this.audioClient = device.AudioClient;
+ this.mmDevice = device;
+ this.shareMode = shareMode;
+ this.isUsingEventSync = useEventSync;
+ this.latencyMilliseconds = latency;
+ this.syncContext = SynchronizationContext.Current;
+ }
+
+ static MMDevice GetDefaultAudioEndpoint()
+ {
+ if (Environment.OSVersion.Version.Major < 6)
+ {
+ throw new NotSupportedException("WASAPI supported only on Windows Vista and above");
+ }
+ var enumerator = new MMDeviceEnumerator();
+ return enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
+ }
+
+ private void PlayThread()
+ {
+ ResamplerDmoStream resamplerDmoStream = null;
+ IWaveProvider playbackProvider = this.sourceProvider;
+ Exception exception = null;
+ try
+ {
+ if (this.dmoResamplerNeeded)
+ {
+ resamplerDmoStream = new ResamplerDmoStream(sourceProvider, outputFormat);
+ playbackProvider = resamplerDmoStream;
+ }
+
+ // fill a whole buffer
+ bufferFrameCount = audioClient.BufferSize;
+ bytesPerFrame = outputFormat.Channels * outputFormat.BitsPerSample / 8;
+ readBuffer = new byte[bufferFrameCount * bytesPerFrame];
+ FillBuffer(playbackProvider, bufferFrameCount);
+
+ // Create WaitHandle for sync
+ var waitHandles = new WaitHandle[] { frameEventWaitHandle };
+
+ audioClient.Start();
+
+ while (playbackState != PlaybackState.Stopped)
+ {
+ // If using Event Sync, Wait for notification from AudioClient or Sleep half latency
+ int indexHandle = 0;
+ if (isUsingEventSync)
+ {
+ indexHandle = WaitHandle.WaitAny(waitHandles, 3 * latencyMilliseconds, false);
+ }
+ else
+ {
+ Thread.Sleep(latencyMilliseconds / 2);
+ }
+
+ // If still playing and notification is ok
+ if (playbackState == PlaybackState.Playing && indexHandle != WaitHandle.WaitTimeout)
+ {
+ // See how much buffer space is available.
+ int numFramesPadding = 0;
+ if (isUsingEventSync)
+ {
+ // In exclusive mode, always ask the max = bufferFrameCount = audioClient.BufferSize
+ numFramesPadding = (shareMode == AudioClientShareMode.Shared) ? audioClient.CurrentPadding : 0;
+ }
+ else
+ {
+ numFramesPadding = audioClient.CurrentPadding;
+ }
+ int numFramesAvailable = bufferFrameCount - numFramesPadding;
+ if (numFramesAvailable > 10) // see https://naudio.codeplex.com/workitem/16363
+ {
+ FillBuffer(playbackProvider, numFramesAvailable);
+ }
+ }
+ }
+ Thread.Sleep(latencyMilliseconds / 2);
+ audioClient.Stop();
+ if (playbackState == PlaybackState.Stopped)
+ {
+ audioClient.Reset();
+ }
+ }
+ catch (Exception e)
+ {
+ exception = e;
+ }
+ finally
+ {
+ if (resamplerDmoStream != null)
+ {
+ resamplerDmoStream.Dispose();
+ }
+ RaisePlaybackStopped(exception);
+ }
+ }
+
+ private void RaisePlaybackStopped(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+
+ private void FillBuffer(IWaveProvider playbackProvider, int frameCount)
+ {
+ IntPtr buffer = renderClient.GetBuffer(frameCount);
+ int readLength = frameCount * bytesPerFrame;
+ int read = playbackProvider.Read(readBuffer, 0, readLength);
+ if (read == 0)
+ {
+ playbackState = PlaybackState.Stopped;
+ }
+ Marshal.Copy(readBuffer,0,buffer,read);
+ int actualFrameCount = read / bytesPerFrame;
+ /*if (actualFrameCount != frameCount)
+ {
+ Debug.WriteLine(String.Format("WASAPI wanted {0} frames, supplied {1}", frameCount, actualFrameCount ));
+ }*/
+ renderClient.ReleaseBuffer(actualFrameCount, AudioClientBufferFlags.None);
+ }
+
+ ///
+ /// Gets the current position in bytes from the wave output device.
+ /// (n.b. this is not the same thing as the position within your reader
+ /// stream)
+ ///
+ /// Position in bytes
+ public long GetPosition()
+ {
+ if (playbackState == Wave.PlaybackState.Stopped)
+ {
+ return 0;
+ }
+ return (long)audioClient.AudioClockClient.AdjustedPosition;
+ }
+
+ ///
+ /// Gets a instance indicating the format the hardware is using.
+ ///
+ public WaveFormat OutputWaveFormat
+ {
+ get { return outputFormat; }
+ }
+
+ #region IWavePlayer Members
+
+ ///
+ /// Begin Playback
+ ///
+ public void Play()
+ {
+ if (playbackState != PlaybackState.Playing)
+ {
+ if (playbackState == PlaybackState.Stopped)
+ {
+ playThread = new Thread(PlayThread);
+ playbackState = PlaybackState.Playing;
+ playThread.Start();
+ }
+ else
+ {
+ playbackState = PlaybackState.Playing;
+ }
+
+
+ }
+ }
+
+ ///
+ /// Stop playback and flush buffers
+ ///
+ public void Stop()
+ {
+ if (playbackState != PlaybackState.Stopped)
+ {
+ playbackState = PlaybackState.Stopped;
+ playThread.Join();
+ playThread = null;
+ }
+ }
+
+ ///
+ /// Stop playback without flushing buffers
+ ///
+ public void Pause()
+ {
+ if (playbackState == PlaybackState.Playing)
+ {
+ playbackState = PlaybackState.Paused;
+ }
+
+ }
+
+ ///
+ /// Initialize for playing the specified wave stream
+ ///
+ /// IWaveProvider to play
+ public void Init(IWaveProvider waveProvider)
+ {
+ long latencyRefTimes = latencyMilliseconds * 10000;
+ outputFormat = waveProvider.WaveFormat;
+ // first attempt uses the WaveFormat from the WaveStream
+ WaveFormatExtensible closestSampleRateFormat;
+ if (!audioClient.IsFormatSupported(shareMode, outputFormat, out closestSampleRateFormat))
+ {
+ // Use closesSampleRateFormat (in sharedMode, it equals usualy to the audioClient.MixFormat)
+ // See documentation : http://msdn.microsoft.com/en-us/library/ms678737(VS.85).aspx
+ // They say : "In shared mode, the audio engine always supports the mix format"
+ // The MixFormat is more likely to be a WaveFormatExtensible.
+ if (closestSampleRateFormat == null)
+ {
+ WaveFormat correctSampleRateFormat = audioClient.MixFormat;
+ /*WaveFormat.CreateIeeeFloatWaveFormat(
+ audioClient.MixFormat.SampleRate,
+ audioClient.MixFormat.Channels);*/
+
+ if (!audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ // Iterate from Worst to Best Format
+ WaveFormatExtensible[] bestToWorstFormats = {
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 32,
+ outputFormat.Channels),
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 24,
+ outputFormat.Channels),
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 16,
+ outputFormat.Channels),
+ };
+
+ // Check from best Format to worst format ( Float32, Int24, Int16 )
+ for (int i = 0; i < bestToWorstFormats.Length; i++ )
+ {
+ correctSampleRateFormat = bestToWorstFormats[i];
+ if ( audioClient.IsFormatSupported(shareMode, correctSampleRateFormat) )
+ {
+ break;
+ }
+ correctSampleRateFormat = null;
+ }
+
+ // If still null, then test on the PCM16, 2 channels
+ if (correctSampleRateFormat == null)
+ {
+ // Last Last Last Chance (Thanks WASAPI)
+ correctSampleRateFormat = new WaveFormatExtensible(outputFormat.SampleRate, 16, 2);
+ if (!audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ throw new NotSupportedException("Can't find a supported format to use");
+ }
+ }
+ }
+ outputFormat = correctSampleRateFormat;
+ }
+ else
+ {
+ outputFormat = closestSampleRateFormat;
+ }
+
+ // just check that we can make it.
+ using (new ResamplerDmoStream(waveProvider, outputFormat))
+ {
+ }
+ this.dmoResamplerNeeded = true;
+ }
+ else
+ {
+ dmoResamplerNeeded = false;
+ }
+ this.sourceProvider = waveProvider;
+
+ // If using EventSync, setup is specific with shareMode
+ if (isUsingEventSync)
+ {
+ // Init Shared or Exclusive
+ if (shareMode == AudioClientShareMode.Shared)
+ {
+ // With EventCallBack and Shared, both latencies must be set to 0
+ audioClient.Initialize(shareMode, AudioClientStreamFlags.EventCallback, 0, 0,
+ outputFormat, Guid.Empty);
+
+ // Get back the effective latency from AudioClient
+ latencyMilliseconds = (int)(audioClient.StreamLatency / 10000);
+ }
+ else
+ {
+ // With EventCallBack and Exclusive, both latencies must equals
+ audioClient.Initialize(shareMode, AudioClientStreamFlags.EventCallback, latencyRefTimes, latencyRefTimes,
+ outputFormat, Guid.Empty);
+ }
+
+ // Create the Wait Event Handle
+ frameEventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);
+ audioClient.SetEventHandle(frameEventWaitHandle.SafeWaitHandle.DangerousGetHandle());
+ }
+ else
+ {
+ // Normal setup for both sharedMode
+ audioClient.Initialize(shareMode, AudioClientStreamFlags.None, latencyRefTimes, 0,
+ outputFormat, Guid.Empty);
+ }
+
+ // Get the RenderClient
+ renderClient = audioClient.AudioRenderClient;
+ }
+
+ ///
+ /// Playback State
+ ///
+ public PlaybackState PlaybackState
+ {
+ get { return playbackState; }
+ }
+
+ ///
+ /// Volume
+ ///
+ public float Volume
+ {
+ get
+ {
+ return mmDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
+ }
+ set
+ {
+ if (value < 0) throw new ArgumentOutOfRangeException("value", "Volume must be between 0.0 and 1.0");
+ if (value > 1) throw new ArgumentOutOfRangeException("value", "Volume must be between 0.0 and 1.0");
+ mmDevice.AudioEndpointVolume.MasterVolumeLevelScalar = value;
+ }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (audioClient != null)
+ {
+ Stop();
+
+ audioClient.Dispose();
+ audioClient = null;
+ renderClient = null;
+ }
+
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/WaveBuffer.cs b/NAudio/Wave/WaveOutputs/WaveBuffer.cs
new file mode 100644
index 00000000..cb50ee30
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/WaveBuffer.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveBuffer class use to store wave datas. Data can be manipulated with arrays
+ /// ( , , , ) that are pointing to the
+ /// same memory buffer. Use the associated Count property based on the type of buffer to get the number of
+ /// data in the buffer.
+ /// Implicit casting is now supported to float[], byte[], int[], short[].
+ /// You must not use Length on returned arrays.
+ ///
+ /// n.b. FieldOffset is 8 now to allow it to work natively on 64 bit
+ ///
+ [StructLayout(LayoutKind.Explicit, Pack = 2)]
+ public class WaveBuffer : IWaveBuffer
+ {
+ ///
+ /// Number of Bytes
+ ///
+ [FieldOffset(0)]
+ public int numberOfBytes;
+ [FieldOffset(8)]
+ private byte[] byteBuffer;
+ [FieldOffset(8)]
+ private float[] floatBuffer;
+ [FieldOffset(8)]
+ private short[] shortBuffer;
+ [FieldOffset(8)]
+ private int[] intBuffer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The number of bytes. The size of the final buffer will be aligned on 4 Bytes (upper bound)
+ public WaveBuffer(int sizeToAllocateInBytes)
+ {
+ int aligned4Bytes = sizeToAllocateInBytes%4;
+ sizeToAllocateInBytes = (aligned4Bytes == 0) ? sizeToAllocateInBytes : sizeToAllocateInBytes + 4 - aligned4Bytes;
+ // Allocating the byteBuffer is co-allocating the floatBuffer and the intBuffer
+ byteBuffer = new byte[sizeToAllocateInBytes];
+ numberOfBytes = 0;
+ }
+
+ ///
+ /// Initializes a new instance of the class binded to a specific byte buffer.
+ ///
+ /// A byte buffer to bound the WaveBuffer to.
+ public WaveBuffer(byte[] bufferToBoundTo)
+ {
+ BindTo(bufferToBoundTo);
+ }
+
+ ///
+ /// Binds this WaveBuffer instance to a specific byte buffer.
+ ///
+ /// A byte buffer to bound the WaveBuffer to.
+ public void BindTo(byte[] bufferToBoundTo)
+ {
+ /* WaveBuffer assumes the caller knows what they are doing. We will let this pass
+ * if ( (bufferToBoundTo.Length % 4) != 0 )
+ {
+ throw new ArgumentException("The byte buffer to bound must be 4 bytes aligned");
+ }*/
+ byteBuffer = bufferToBoundTo;
+ numberOfBytes = 0;
+ }
+
+ ///
+ /// Performs an implicit conversion from to .
+ ///
+ /// The wave buffer.
+ /// The result of the conversion.
+ public static implicit operator byte[](WaveBuffer waveBuffer)
+ {
+ return waveBuffer.byteBuffer;
+ }
+
+ ///
+ /// Performs an implicit conversion from to .
+ ///
+ /// The wave buffer.
+ /// The result of the conversion.
+ public static implicit operator float[](WaveBuffer waveBuffer)
+ {
+ return waveBuffer.floatBuffer;
+ }
+
+ ///
+ /// Performs an implicit conversion from to .
+ ///
+ /// The wave buffer.
+ /// The result of the conversion.
+ public static implicit operator int[](WaveBuffer waveBuffer)
+ {
+ return waveBuffer.intBuffer;
+ }
+
+ ///
+ /// Performs an implicit conversion from to .
+ ///
+ /// The wave buffer.
+ /// The result of the conversion.
+ public static implicit operator short[](WaveBuffer waveBuffer)
+ {
+ return waveBuffer.shortBuffer;
+ }
+
+ ///
+ /// Gets the byte buffer.
+ ///
+ /// The byte buffer.
+ public byte[] ByteBuffer
+ {
+ get { return byteBuffer; }
+ }
+
+ ///
+ /// Gets the float buffer.
+ ///
+ /// The float buffer.
+ public float[] FloatBuffer
+ {
+ get { return floatBuffer; }
+ }
+
+ ///
+ /// Gets the short buffer.
+ ///
+ /// The short buffer.
+ public short[] ShortBuffer
+ {
+ get { return shortBuffer; }
+ }
+
+ ///
+ /// Gets the int buffer.
+ ///
+ /// The int buffer.
+ public int[] IntBuffer
+ {
+ get { return intBuffer; }
+ }
+
+
+ ///
+ /// Gets the max size in bytes of the byte buffer..
+ ///
+ /// Maximum number of bytes in the buffer.
+ public int MaxSize
+ {
+ get { return byteBuffer.Length; }
+ }
+
+ ///
+ /// Gets or sets the byte buffer count.
+ ///
+ /// The byte buffer count.
+ public int ByteBufferCount
+ {
+ get { return numberOfBytes; }
+ set
+ {
+ numberOfBytes = CheckValidityCount("ByteBufferCount", value, 1);
+ }
+ }
+ ///
+ /// Gets or sets the float buffer count.
+ ///
+ /// The float buffer count.
+ public int FloatBufferCount
+ {
+ get { return numberOfBytes / 4; }
+ set
+ {
+ numberOfBytes = CheckValidityCount("FloatBufferCount", value, 4);
+ }
+ }
+ ///
+ /// Gets or sets the short buffer count.
+ ///
+ /// The short buffer count.
+ public int ShortBufferCount
+ {
+ get { return numberOfBytes / 2; }
+ set
+ {
+ numberOfBytes = CheckValidityCount("ShortBufferCount", value, 2);
+ }
+ }
+ ///
+ /// Gets or sets the int buffer count.
+ ///
+ /// The int buffer count.
+ public int IntBufferCount
+ {
+ get { return numberOfBytes / 4; }
+ set
+ {
+ numberOfBytes = CheckValidityCount("IntBufferCount", value, 4);
+ }
+ }
+
+ ///
+ /// Clears the associated buffer.
+ ///
+ public void Clear()
+ {
+ Array.Clear(byteBuffer, 0, byteBuffer.Length);
+ }
+
+ ///
+ /// Copy this WaveBuffer to a destination buffer up to ByteBufferCount bytes.
+ ///
+ public void Copy(Array destinationArray)
+ {
+ Array.Copy(byteBuffer, destinationArray, numberOfBytes);
+ }
+
+ ///
+ /// Checks the validity of the count parameters.
+ ///
+ /// Name of the arg.
+ /// The value.
+ /// The size of value.
+ private int CheckValidityCount(string argName, int value, int sizeOfValue)
+ {
+ int newNumberOfBytes = value * sizeOfValue;
+ if ( (newNumberOfBytes % 4) != 0 )
+ {
+ throw new ArgumentOutOfRangeException(argName, String.Format("{0} cannot set a count ({1}) that is not 4 bytes aligned ", argName, newNumberOfBytes));
+ }
+
+ if (value < 0 || value > (byteBuffer.Length / sizeOfValue))
+ {
+ throw new ArgumentOutOfRangeException(argName, String.Format("{0} cannot set a count that exceed max count {1}", argName, byteBuffer.Length / sizeOfValue));
+ }
+ return newNumberOfBytes;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/WaveFileWriter.cs b/NAudio/Wave/WaveOutputs/WaveFileWriter.cs
new file mode 100644
index 00000000..ea3396cc
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/WaveFileWriter.cs
@@ -0,0 +1,427 @@
+using System;
+using System.IO;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudio.Wave
+{
+ ///
+ /// This class writes WAV data to a .wav file on disk
+ ///
+ public class WaveFileWriter : Stream
+ {
+ private Stream outStream;
+ private readonly BinaryWriter writer;
+ private long dataSizePos;
+ private long factSampleCountPos;
+ private long dataChunkSize;
+ private readonly WaveFormat format;
+ private readonly string filename;
+
+ ///
+ /// Creates a 16 bit Wave File from an ISampleProvider
+ /// BEWARE: the source provider must not return data indefinitely
+ ///
+ /// The filename to write to
+ /// The source sample provider
+ public static void CreateWaveFile16(string filename, ISampleProvider sourceProvider)
+ {
+ CreateWaveFile(filename, new SampleToWaveProvider16(sourceProvider));
+ }
+
+ ///
+ /// Creates a Wave file by reading all the data from a WaveProvider
+ /// BEWARE: the WaveProvider MUST return 0 from its Read method when it is finished,
+ /// or the Wave File will grow indefinitely.
+ ///
+ /// The filename to use
+ /// The source WaveProvider
+ public static void CreateWaveFile(string filename, IWaveProvider sourceProvider)
+ {
+ using (var writer = new WaveFileWriter(filename, sourceProvider.WaveFormat))
+ {
+ long outputLength = 0;
+ var buffer = new byte[sourceProvider.WaveFormat.AverageBytesPerSecond * 4];
+ while (true)
+ {
+ int bytesRead = sourceProvider.Read(buffer, 0, buffer.Length);
+ if (bytesRead == 0)
+ {
+ // end of source provider
+ break;
+ }
+ outputLength += bytesRead;
+ // Write will throw exception if WAV file becomes too large
+ writer.Write(buffer, 0, bytesRead);
+ }
+ }
+ }
+
+ ///
+ /// WaveFileWriter that actually writes to a stream
+ ///
+ /// Stream to be written to
+ /// Wave format to use
+ public WaveFileWriter(Stream outStream, WaveFormat format)
+ {
+ this.outStream = outStream;
+ this.format = format;
+ this.writer = new BinaryWriter(outStream, System.Text.Encoding.UTF8);
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("RIFF"));
+ this.writer.Write((int)0); // placeholder
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("WAVE"));
+
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("fmt "));
+ format.Serialize(this.writer);
+
+ CreateFactChunk();
+ WriteDataChunkHeader();
+ }
+
+ ///
+ /// Creates a new WaveFileWriter
+ ///
+ /// The filename to write to
+ /// The Wave Format of the output data
+ public WaveFileWriter(string filename, WaveFormat format)
+ : this(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Read), format)
+ {
+ this.filename = filename;
+ }
+
+ private void WriteDataChunkHeader()
+ {
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("data"));
+ dataSizePos = this.outStream.Position;
+ this.writer.Write((int)0); // placeholder
+ }
+
+ private void CreateFactChunk()
+ {
+ if (HasFactChunk())
+ {
+ this.writer.Write(System.Text.Encoding.UTF8.GetBytes("fact"));
+ this.writer.Write((int)4);
+ factSampleCountPos = this.outStream.Position;
+ this.writer.Write((int)0); // number of samples
+ }
+ }
+
+ private bool HasFactChunk()
+ {
+ return format.Encoding != WaveFormatEncoding.Pcm &&
+ format.BitsPerSample != 0;
+ }
+
+ ///
+ /// The wave file name or null if not applicable
+ ///
+ public string Filename
+ {
+ get { return filename; }
+ }
+
+ ///
+ /// Number of bytes of audio in the data chunk
+ ///
+ public override long Length
+ {
+ get { return dataChunkSize; }
+ }
+
+ ///
+ /// WaveFormat of this wave file
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return format; }
+ }
+
+ ///
+ /// Returns false: Cannot read from a WaveFileWriter
+ ///
+ public override bool CanRead
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Returns true: Can write to a WaveFileWriter
+ ///
+ public override bool CanWrite
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Returns false: Cannot seek within a WaveFileWriter
+ ///
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ ///
+ /// Read is not supported for a WaveFileWriter
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new InvalidOperationException("Cannot read from a WaveFileWriter");
+ }
+
+ ///
+ /// Seek is not supported for a WaveFileWriter
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new InvalidOperationException("Cannot seek within a WaveFileWriter");
+ }
+
+ ///
+ /// SetLength is not supported for WaveFileWriter
+ ///
+ ///
+ public override void SetLength(long value)
+ {
+ throw new InvalidOperationException("Cannot set length of a WaveFileWriter");
+ }
+
+ ///
+ /// Gets the Position in the WaveFile (i.e. number of bytes written so far)
+ ///
+ public override long Position
+ {
+ get { return dataChunkSize; }
+ set { throw new InvalidOperationException("Repositioning a WaveFileWriter is not supported"); }
+ }
+
+ ///
+ /// Appends bytes to the WaveFile (assumes they are already in the correct format)
+ ///
+ /// the buffer containing the wave data
+ /// the offset from which to start writing
+ /// the number of bytes to write
+ [Obsolete("Use Write instead")]
+ public void WriteData(byte[] data, int offset, int count)
+ {
+ Write(data, offset, count);
+ }
+
+ ///
+ /// Appends bytes to the WaveFile (assumes they are already in the correct format)
+ ///
+ /// the buffer containing the wave data
+ /// the offset from which to start writing
+ /// the number of bytes to write
+ public override void Write(byte[] data, int offset, int count)
+ {
+ if (outStream.Length + count > UInt32.MaxValue)
+ throw new ArgumentException("WAV file too large", "count");
+ outStream.Write(data, offset, count);
+ dataChunkSize += count;
+ }
+
+ private readonly byte[] value24 = new byte[3]; // keep this around to save us creating it every time
+
+ ///
+ /// Writes a single sample to the Wave file
+ ///
+ /// the sample to write (assumed floating point with 1.0f as max value)
+ public void WriteSample(float sample)
+ {
+ if (WaveFormat.BitsPerSample == 16)
+ {
+ writer.Write((Int16)(Int16.MaxValue * sample));
+ dataChunkSize += 2;
+ }
+ else if (WaveFormat.BitsPerSample == 24)
+ {
+ var value = BitConverter.GetBytes((Int32)(Int32.MaxValue * sample));
+ value24[0] = value[1];
+ value24[1] = value[2];
+ value24[2] = value[3];
+ writer.Write(value24);
+ dataChunkSize += 3;
+ }
+ else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible)
+ {
+ writer.Write(UInt16.MaxValue * (Int32)sample);
+ dataChunkSize += 4;
+ }
+ else if (WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ writer.Write(sample);
+ dataChunkSize += 4;
+ }
+ else
+ {
+ throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported");
+ }
+ }
+
+ ///
+ /// Writes 32 bit floating point samples to the Wave file
+ /// They will be converted to the appropriate bit depth depending on the WaveFormat of the WAV file
+ ///
+ /// The buffer containing the floating point samples
+ /// The offset from which to start writing
+ /// The number of floating point samples to write
+ public void WriteSamples(float[] samples, int offset, int count)
+ {
+ for (int n = 0; n < count; n++)
+ {
+ WriteSample(samples[offset + n]);
+ }
+ }
+
+ ///
+ /// Writes 16 bit samples to the Wave file
+ ///
+ /// The buffer containing the 16 bit samples
+ /// The offset from which to start writing
+ /// The number of 16 bit samples to write
+ [Obsolete("Use WriteSamples instead")]
+ public void WriteData(short[] samples, int offset, int count)
+ {
+ WriteSamples(samples, offset, count);
+ }
+
+
+ ///
+ /// Writes 16 bit samples to the Wave file
+ ///
+ /// The buffer containing the 16 bit samples
+ /// The offset from which to start writing
+ /// The number of 16 bit samples to write
+ public void WriteSamples(short[] samples, int offset, int count)
+ {
+ // 16 bit PCM data
+ if (WaveFormat.BitsPerSample == 16)
+ {
+ for (int sample = 0; sample < count; sample++)
+ {
+ writer.Write(samples[sample + offset]);
+ }
+ dataChunkSize += (count * 2);
+ }
+ // 24 bit PCM data
+ else if (WaveFormat.BitsPerSample == 24)
+ {
+ byte[] value;
+ for (int sample = 0; sample < count; sample++)
+ {
+ value = BitConverter.GetBytes(UInt16.MaxValue * (Int32)samples[sample + offset]);
+ value24[0] = value[1];
+ value24[1] = value[2];
+ value24[2] = value[3];
+ writer.Write(value24);
+ }
+ dataChunkSize += (count * 3);
+ }
+ // 32 bit PCM data
+ else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.Extensible)
+ {
+ for (int sample = 0; sample < count; sample++)
+ {
+ writer.Write(UInt16.MaxValue * (Int32)samples[sample + offset]);
+ }
+ dataChunkSize += (count * 4);
+ }
+ // IEEE float data
+ else if (WaveFormat.BitsPerSample == 32 && WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ for (int sample = 0; sample < count; sample++)
+ {
+ writer.Write((float)samples[sample + offset] / (float)(Int16.MaxValue + 1));
+ }
+ dataChunkSize += (count * 4);
+ }
+ else
+ {
+ throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported");
+ }
+ }
+
+ ///
+ /// Ensures data is written to disk
+ ///
+ public override void Flush()
+ {
+ writer.Flush();
+ }
+
+ #region IDisposable Members
+
+ ///
+ /// Actually performs the close,making sure the header contains the correct data
+ ///
+ /// True if called from Dispose
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (outStream != null)
+ {
+ try
+ {
+ UpdateHeader(writer);
+ }
+ finally
+ {
+ // in a finally block as we don't want the FileStream to run its disposer in
+ // the GC thread if the code above caused an IOException (e.g. due to disk full)
+ outStream.Close(); // will close the underlying base stream
+ outStream = null;
+ }
+ }
+ }
+ }
+
+ ///
+ /// Updates the header with file size information
+ ///
+ protected virtual void UpdateHeader(BinaryWriter writer)
+ {
+ this.Flush();
+ UpdateRiffChunk(writer);
+ UpdateFactChunk(writer);
+ UpdateDataChunk(writer);
+ }
+
+ private void UpdateDataChunk(BinaryWriter writer)
+ {
+ writer.Seek((int)dataSizePos, SeekOrigin.Begin);
+ writer.Write((UInt32)dataChunkSize);
+ }
+
+ private void UpdateRiffChunk(BinaryWriter writer)
+ {
+ writer.Seek(4, SeekOrigin.Begin);
+ writer.Write((UInt32)(outStream.Length - 8));
+ }
+
+ private void UpdateFactChunk(BinaryWriter writer)
+ {
+ if (HasFactChunk())
+ {
+ int bitsPerSample = (format.BitsPerSample * format.Channels);
+ if (bitsPerSample != 0)
+ {
+ writer.Seek((int)factSampleCountPos, SeekOrigin.Begin);
+
+ writer.Write((int)((dataChunkSize * 8) / bitsPerSample));
+ }
+ }
+ }
+
+ ///
+ /// Finaliser - should only be called if the user forgot to close this WaveFileWriter
+ ///
+ ~WaveFileWriter()
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveFileWriter was not disposed");
+ Dispose(false);
+ }
+
+ #endregion
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/WaveOut.cs b/NAudio/Wave/WaveOutputs/WaveOut.cs
new file mode 100644
index 00000000..b139ec1d
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/WaveOut.cs
@@ -0,0 +1,441 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Threading;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Represents a wave out device
+ ///
+ public class WaveOut : IWavePlayer, IWavePosition
+ {
+ private IntPtr hWaveOut;
+ private WaveOutBuffer[] buffers;
+ private IWaveProvider waveStream;
+ private volatile PlaybackState playbackState;
+ private WaveInterop.WaveCallback callback;
+ private float volume = 1;
+ private WaveCallbackInfo callbackInfo;
+ private object waveOutLock;
+ private int queuedBuffers;
+ private SynchronizationContext syncContext;
+
+ ///
+ /// Indicates playback has stopped automatically
+ ///
+ public event EventHandler PlaybackStopped;
+
+ ///
+ /// Retrieves the capabilities of a waveOut device
+ ///
+ /// Device to test
+ /// The WaveOut device capabilities
+ public static WaveOutCapabilities GetCapabilities(int devNumber)
+ {
+ WaveOutCapabilities caps = new WaveOutCapabilities();
+ int structSize = Marshal.SizeOf(caps);
+ MmException.Try(WaveInterop.waveOutGetDevCaps((IntPtr)devNumber, out caps, structSize), "waveOutGetDevCaps");
+ return caps;
+ }
+
+ ///
+ /// Returns the number of Wave Out devices available in the system
+ ///
+ public static Int32 DeviceCount
+ {
+ get
+ {
+ return WaveInterop.waveOutGetNumDevs();
+ }
+ }
+
+ ///
+ /// Gets or sets the desired latency in milliseconds
+ /// Should be set before a call to Init
+ ///
+ public int DesiredLatency { get; set; }
+
+ ///
+ /// Gets or sets the number of buffers used
+ /// Should be set before a call to Init
+ ///
+ public int NumberOfBuffers { get; set; }
+
+ ///
+ /// Gets or sets the device number
+ /// Should be set before a call to Init
+ /// This must be between 0 and DeviceCount - 1.
+ ///
+ public int DeviceNumber { get; set; }
+
+
+ ///
+ /// Creates a default WaveOut device
+ /// Will use window callbacks if called from a GUI thread, otherwise function
+ /// callbacks
+ ///
+ public WaveOut()
+ : this(SynchronizationContext.Current == null ? WaveCallbackInfo.FunctionCallback() : WaveCallbackInfo.NewWindow())
+ {
+ }
+
+ ///
+ /// Creates a WaveOut device using the specified window handle for callbacks
+ ///
+ /// A valid window handle
+ public WaveOut(IntPtr windowHandle)
+ : this(WaveCallbackInfo.ExistingWindow(windowHandle))
+ {
+
+ }
+
+ ///
+ /// Opens a WaveOut device
+ ///
+ public WaveOut(WaveCallbackInfo callbackInfo)
+ {
+ this.syncContext = SynchronizationContext.Current;
+ // set default values up
+ this.DeviceNumber = 0;
+ this.DesiredLatency = 300;
+ this.NumberOfBuffers = 2;
+
+ this.callback = new WaveInterop.WaveCallback(Callback);
+ this.waveOutLock = new object();
+ this.callbackInfo = callbackInfo;
+ callbackInfo.Connect(this.callback);
+ }
+
+ ///
+ /// Initialises the WaveOut device
+ ///
+ /// WaveProvider to play
+ public void Init(IWaveProvider waveProvider)
+ {
+ this.waveStream = waveProvider;
+ int bufferSize = waveProvider.WaveFormat.ConvertLatencyToByteSize((DesiredLatency + NumberOfBuffers - 1) / NumberOfBuffers);
+
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = callbackInfo.WaveOutOpen(out hWaveOut, DeviceNumber, waveStream.WaveFormat, callback);
+ }
+ MmException.Try(result, "waveOutOpen");
+
+ buffers = new WaveOutBuffer[NumberOfBuffers];
+ playbackState = PlaybackState.Stopped;
+ for (int n = 0; n < NumberOfBuffers; n++)
+ {
+ buffers[n] = new WaveOutBuffer(hWaveOut, bufferSize, waveStream, waveOutLock);
+ }
+ }
+
+ ///
+ /// Start playing the audio from the WaveStream
+ ///
+ public void Play()
+ {
+ if (playbackState == PlaybackState.Stopped)
+ {
+ playbackState = PlaybackState.Playing;
+ Debug.Assert(queuedBuffers == 0, "Buffers already queued on play");
+ EnqueueBuffers();
+ }
+ else if (playbackState == PlaybackState.Paused)
+ {
+ EnqueueBuffers();
+ Resume();
+ playbackState = PlaybackState.Playing;
+ }
+ }
+
+ private void EnqueueBuffers()
+ {
+ for (int n = 0; n < NumberOfBuffers; n++)
+ {
+ if (!buffers[n].InQueue)
+ {
+ if (buffers[n].OnDone())
+ {
+ Interlocked.Increment(ref queuedBuffers);
+ }
+ else
+ {
+ playbackState = PlaybackState.Stopped;
+ break;
+ }
+ //Debug.WriteLine(String.Format("Resume from Pause: Buffer [{0}] requeued", n));
+ }
+ else
+ {
+ //Debug.WriteLine(String.Format("Resume from Pause: Buffer [{0}] already queued", n));
+ }
+ }
+ }
+
+ ///
+ /// Pause the audio
+ ///
+ public void Pause()
+ {
+ if (playbackState == PlaybackState.Playing)
+ {
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutPause(hWaveOut);
+ }
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "waveOutPause");
+ }
+ playbackState = PlaybackState.Paused;
+ }
+ }
+
+ ///
+ /// Resume playing after a pause from the same position
+ ///
+ public void Resume()
+ {
+ if (playbackState == PlaybackState.Paused)
+ {
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutRestart(hWaveOut);
+ }
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "waveOutRestart");
+ }
+ playbackState = PlaybackState.Playing;
+ }
+ }
+
+ ///
+ /// Stop and reset the WaveOut device
+ ///
+ public void Stop()
+ {
+ if (playbackState != PlaybackState.Stopped)
+ {
+
+ // in the call to waveOutReset with function callbacks
+ // some drivers will block here until OnDone is called
+ // for every buffer
+ playbackState = PlaybackState.Stopped; // set this here to avoid a problem with some drivers whereby
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutReset(hWaveOut);
+ }
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "waveOutReset");
+ }
+
+ // with function callbacks, waveOutReset will call OnDone,
+ // and so PlaybackStopped must not be raised from the handler
+ // we know playback has definitely stopped now, so raise callback
+ if (callbackInfo.Strategy == WaveCallbackStrategy.FunctionCallback)
+ {
+ RaisePlaybackStoppedEvent(null);
+ }
+ }
+ }
+
+ ///
+ /// Gets the current position in bytes from the wave output device.
+ /// (n.b. this is not the same thing as the position within your reader
+ /// stream - it calls directly into waveOutGetPosition)
+ ///
+ /// Position in bytes
+ public long GetPosition()
+ {
+ lock (waveOutLock)
+ {
+ MmTime mmTime = new MmTime();
+ mmTime.wType = MmTime.TIME_BYTES; // request results in bytes, TODO: perhaps make this a little more flexible and support the other types?
+ MmException.Try(WaveInterop.waveOutGetPosition(hWaveOut, out mmTime, Marshal.SizeOf(mmTime)), "waveOutGetPosition");
+
+ if (mmTime.wType != MmTime.TIME_BYTES)
+ throw new Exception(string.Format("waveOutGetPosition: wType -> Expected {0}, Received {1}", MmTime.TIME_BYTES, mmTime.wType));
+
+ return mmTime.cb;
+ }
+ }
+
+ ///
+ /// Gets a instance indicating the format the hardware is using.
+ ///
+ public WaveFormat OutputWaveFormat
+ {
+ get { return this.waveStream.WaveFormat; }
+ }
+
+ ///
+ /// Playback State
+ ///
+ public PlaybackState PlaybackState
+ {
+ get { return playbackState; }
+ }
+
+ ///
+ /// Volume for this device 1.0 is full scale
+ ///
+ public float Volume
+ {
+ get
+ {
+ return volume;
+ }
+ set
+ {
+ SetWaveOutVolume(value, hWaveOut, waveOutLock);
+ volume = value;
+ }
+ }
+
+ internal static void SetWaveOutVolume(float value, IntPtr hWaveOut, object lockObject)
+ {
+ if (value < 0) throw new ArgumentOutOfRangeException("value", "Volume must be between 0.0 and 1.0");
+ if (value > 1) throw new ArgumentOutOfRangeException("value", "Volume must be between 0.0 and 1.0");
+ float left = value;
+ float right = value;
+
+ int stereoVolume = (int) (left*0xFFFF) + ((int) (right*0xFFFF) << 16);
+ MmResult result;
+ lock (lockObject)
+ {
+ result = WaveInterop.waveOutSetVolume(hWaveOut, stereoVolume);
+ }
+ MmException.Try(result, "waveOutSetVolume");
+ }
+
+ #region Dispose Pattern
+
+ ///
+ /// Closes this WaveOut device
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ Dispose(true);
+ }
+
+ ///
+ /// Closes the WaveOut device and disposes of buffers
+ ///
+ /// True if called from Dispose
+ protected void Dispose(bool disposing)
+ {
+ Stop();
+
+ if (disposing)
+ {
+ if (buffers != null)
+ {
+ for (int n = 0; n < buffers.Length; n++)
+ {
+ if (buffers[n] != null)
+ {
+ buffers[n].Dispose();
+ }
+ }
+ buffers = null;
+ }
+ }
+
+ lock (waveOutLock)
+ {
+ WaveInterop.waveOutClose(hWaveOut);
+ }
+ if (disposing)
+ {
+ callbackInfo.Disconnect();
+ }
+ }
+
+ ///
+ /// Finalizer. Only called when user forgets to call Dispose
+ ///
+ ~WaveOut()
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveOut device was not closed");
+ Dispose(false);
+ }
+
+ #endregion
+
+ // made non-static so that playing can be stopped here
+ private void Callback(IntPtr hWaveOut, WaveInterop.WaveMessage uMsg, IntPtr dwInstance, WaveHeader wavhdr, IntPtr dwReserved)
+ {
+ if (uMsg == WaveInterop.WaveMessage.WaveOutDone)
+ {
+ GCHandle hBuffer = (GCHandle)wavhdr.userData;
+ WaveOutBuffer buffer = (WaveOutBuffer)hBuffer.Target;
+ Interlocked.Decrement(ref queuedBuffers);
+ Exception exception = null;
+ // check that we're not here through pressing stop
+ if (PlaybackState == PlaybackState.Playing)
+ {
+ // to avoid deadlocks in Function callback mode,
+ // we lock round this whole thing, which will include the
+ // reading from the stream.
+ // this protects us from calling waveOutReset on another
+ // thread while a WaveOutWrite is in progress
+ lock (waveOutLock)
+ {
+ try
+ {
+ if (buffer.OnDone())
+ {
+ Interlocked.Increment(ref queuedBuffers);
+ }
+ }
+ catch (Exception e)
+ {
+ // one likely cause is soundcard being unplugged
+ exception = e;
+ }
+ }
+ }
+ if (queuedBuffers == 0)
+ {
+ if (callbackInfo.Strategy == WaveCallbackStrategy.FunctionCallback && playbackState == Wave.PlaybackState.Stopped)
+ {
+ // the user has pressed stop
+ // DO NOT raise the playback stopped event from here
+ // since on the main thread we are still in the waveOutReset function
+ // Playback stopped will be raised elsewhere
+ }
+ else
+ {
+ playbackState = PlaybackState.Stopped; // set explicitly for when we reach the end of the audio
+ RaisePlaybackStoppedEvent(exception);
+ }
+ }
+ }
+ }
+
+ private void RaisePlaybackStoppedEvent(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ if (this.syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ this.syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveOutputs/WaveOutEvent.cs b/NAudio/Wave/WaveOutputs/WaveOutEvent.cs
new file mode 100644
index 00000000..c76d1937
--- /dev/null
+++ b/NAudio/Wave/WaveOutputs/WaveOutEvent.cs
@@ -0,0 +1,374 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Runtime.InteropServices;
+using System.Windows.Forms.VisualStyles;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Alternative WaveOut class, making use of the Event callback
+ ///
+ public class WaveOutEvent : IWavePlayer, IWavePosition
+ {
+ private readonly object waveOutLock;
+ private readonly SynchronizationContext syncContext;
+ private IntPtr hWaveOut; // WaveOut handle
+ private WaveOutBuffer[] buffers;
+ private IWaveProvider waveStream;
+ private volatile PlaybackState playbackState;
+ private AutoResetEvent callbackEvent;
+ private float volume = 1.0f;
+
+ ///
+ /// Indicates playback has stopped automatically
+ ///
+ public event EventHandler PlaybackStopped;
+
+ ///
+ /// Gets or sets the desired latency in milliseconds
+ /// Should be set before a call to Init
+ ///
+ public int DesiredLatency { get; set; }
+
+ ///
+ /// Gets or sets the number of buffers used
+ /// Should be set before a call to Init
+ ///
+ public int NumberOfBuffers { get; set; }
+
+ ///
+ /// Gets or sets the device number
+ /// Should be set before a call to Init
+ /// This must be between 0 and DeviceCount - 1.
+ ///
+ public int DeviceNumber { get; set; }
+
+ ///
+ /// Opens a WaveOut device
+ ///
+ public WaveOutEvent()
+ {
+ syncContext = SynchronizationContext.Current;
+ if (syncContext != null &&
+ ((syncContext.GetType().Name == "LegacyAspNetSynchronizationContext") ||
+ (syncContext.GetType().Name == "AspNetSynchronizationContext")))
+ {
+ syncContext = null;
+ }
+
+ // set default values up
+ DeviceNumber = 0;
+ DesiredLatency = 300;
+ NumberOfBuffers = 2;
+
+ this.waveOutLock = new object();
+ }
+
+ ///
+ /// Initialises the WaveOut device
+ ///
+ /// WaveProvider to play
+ public void Init(IWaveProvider waveProvider)
+ {
+ if (playbackState != PlaybackState.Stopped)
+ {
+ throw new InvalidOperationException("Can't re-initialize during playback");
+ }
+ if (hWaveOut != IntPtr.Zero)
+ {
+ // normally we don't allow calling Init twice, but as experiment, see if we can clean up and go again
+ // try to allow reuse of this waveOut device
+ // n.b. risky if Playback thread has not exited
+ DisposeBuffers();
+ CloseWaveOut();
+ }
+
+ this.callbackEvent = new AutoResetEvent(false);
+
+ this.waveStream = waveProvider;
+ int bufferSize = waveProvider.WaveFormat.ConvertLatencyToByteSize((DesiredLatency + NumberOfBuffers - 1) / NumberOfBuffers);
+
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutOpenWindow(out hWaveOut, (IntPtr)DeviceNumber, waveStream.WaveFormat, callbackEvent.SafeWaitHandle.DangerousGetHandle(), IntPtr.Zero, WaveInterop.WaveInOutOpenFlags.CallbackEvent);
+ }
+ MmException.Try(result, "waveOutOpen");
+
+ buffers = new WaveOutBuffer[NumberOfBuffers];
+ playbackState = PlaybackState.Stopped;
+ for (int n = 0; n < NumberOfBuffers; n++)
+ {
+ buffers[n] = new WaveOutBuffer(hWaveOut, bufferSize, waveStream, waveOutLock);
+ }
+ }
+
+ ///
+ /// Start playing the audio from the WaveStream
+ ///
+ public void Play()
+ {
+ if (this.buffers == null || this.waveStream == null)
+ {
+ throw new InvalidOperationException("Must call Init first");
+ }
+ if (playbackState == PlaybackState.Stopped)
+ {
+ playbackState = PlaybackState.Playing;
+ callbackEvent.Set(); // give the thread a kick
+ ThreadPool.QueueUserWorkItem((state) => PlaybackThread(), null);
+ }
+ else if (playbackState == PlaybackState.Paused)
+ {
+ Resume();
+ callbackEvent.Set(); // give the thread a kick
+ }
+ }
+
+ private void PlaybackThread()
+ {
+ Exception exception = null;
+ try
+ {
+ DoPlayback();
+ }
+ catch (Exception e)
+ {
+ exception = e;
+ }
+ finally
+ {
+ playbackState = PlaybackState.Stopped;
+ // we're exiting our background thread
+ RaisePlaybackStoppedEvent(exception);
+ }
+ }
+
+ private void DoPlayback()
+ {
+ while (playbackState != PlaybackState.Stopped)
+ {
+ if (!callbackEvent.WaitOne(DesiredLatency))
+ Debug.WriteLine("WARNING: WaveOutEvent callback event timeout");
+
+ // requeue any buffers returned to us
+ if (playbackState == PlaybackState.Playing)
+ {
+ int queued = 0;
+ foreach (var buffer in buffers)
+ {
+ if (buffer.InQueue || buffer.OnDone())
+ {
+ queued++;
+ }
+ }
+ if (queued == 0)
+ {
+ // we got to the end
+ this.playbackState = PlaybackState.Stopped;
+ callbackEvent.Set();
+ }
+ }
+ }
+ }
+
+ ///
+ /// Pause the audio
+ ///
+ public void Pause()
+ {
+ if (playbackState == PlaybackState.Playing)
+ {
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutPause(hWaveOut);
+ }
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "waveOutPause");
+ }
+ playbackState = PlaybackState.Paused;
+ }
+ }
+
+ ///
+ /// Resume playing after a pause from the same position
+ ///
+ private void Resume()
+ {
+ if (playbackState == PlaybackState.Paused)
+ {
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutRestart(hWaveOut);
+ }
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "waveOutRestart");
+ }
+ playbackState = PlaybackState.Playing;
+ }
+ }
+
+ ///
+ /// Stop and reset the WaveOut device
+ ///
+ public void Stop()
+ {
+ if (playbackState != PlaybackState.Stopped)
+ {
+ // in the call to waveOutReset with function callbacks
+ // some drivers will block here until OnDone is called
+ // for every buffer
+ playbackState = PlaybackState.Stopped; // set this here to avoid a problem with some drivers whereby
+ MmResult result;
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutReset(hWaveOut);
+ }
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "waveOutReset");
+ }
+ callbackEvent.Set(); // give the thread a kick, make sure we exit
+ }
+ }
+
+ ///
+ /// Gets the current position in bytes from the wave output device.
+ /// (n.b. this is not the same thing as the position within your reader
+ /// stream - it calls directly into waveOutGetPosition)
+ ///
+ /// Position in bytes
+ public long GetPosition()
+ {
+ lock (waveOutLock)
+ {
+ var mmTime = new MmTime();
+ mmTime.wType = MmTime.TIME_BYTES; // request results in bytes, TODO: perhaps make this a little more flexible and support the other types?
+ MmException.Try(WaveInterop.waveOutGetPosition(hWaveOut, out mmTime, Marshal.SizeOf(mmTime)), "waveOutGetPosition");
+
+ if (mmTime.wType != MmTime.TIME_BYTES)
+ throw new Exception(string.Format("waveOutGetPosition: wType -> Expected {0}, Received {1}", MmTime.TIME_BYTES, mmTime.wType));
+
+ return mmTime.cb;
+ }
+ }
+
+ ///
+ /// Gets a instance indicating the format the hardware is using.
+ ///
+ public WaveFormat OutputWaveFormat
+ {
+ get { return this.waveStream.WaveFormat; }
+ }
+
+ ///
+ /// Playback State
+ ///
+ public PlaybackState PlaybackState
+ {
+ get { return playbackState; }
+ }
+
+ ///
+ /// Obsolete property
+ ///
+ [Obsolete]
+ public float Volume
+ {
+ get { return volume; }
+ set
+ {
+ WaveOut.SetWaveOutVolume(value, hWaveOut, waveOutLock);
+ volume = value;
+ }
+ }
+
+ #region Dispose Pattern
+
+ ///
+ /// Closes this WaveOut device
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ Dispose(true);
+ }
+
+ ///
+ /// Closes the WaveOut device and disposes of buffers
+ ///
+ /// True if called from Dispose
+ protected void Dispose(bool disposing)
+ {
+ Stop();
+
+ if (disposing)
+ {
+ DisposeBuffers();
+ }
+
+ CloseWaveOut();
+ }
+
+ private void CloseWaveOut()
+ {
+ if (callbackEvent != null)
+ {
+ callbackEvent.Close();
+ callbackEvent = null;
+ }
+ lock (waveOutLock)
+ {
+ if (hWaveOut != IntPtr.Zero)
+ {
+ WaveInterop.waveOutClose(hWaveOut);
+ hWaveOut= IntPtr.Zero;
+ }
+ }
+ }
+
+ private void DisposeBuffers()
+ {
+ if (buffers != null)
+ {
+ foreach (var buffer in buffers)
+ {
+ buffer.Dispose();
+ }
+ buffers = null;
+ }
+ }
+
+ ///
+ /// Finalizer. Only called when user forgets to call Dispose
+ ///
+ ~WaveOutEvent()
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveOutEvent device was not closed");
+ Dispose(false);
+ }
+
+ #endregion
+
+ private void RaisePlaybackStoppedEvent(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ if (syncContext == null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ else
+ {
+ this.syncContext.Post(state => handler(this, new StoppedEventArgs(e)), null);
+ }
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/BufferedWaveProvider.cs b/NAudio/Wave/WaveProviders/BufferedWaveProvider.cs
new file mode 100644
index 00000000..87085a21
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/BufferedWaveProvider.cs
@@ -0,0 +1,127 @@
+using System;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Provides a buffered store of samples
+ /// Read method will return queued samples or fill buffer with zeroes
+ /// Now backed by a circular buffer
+ ///
+ public class BufferedWaveProvider : IWaveProvider
+ {
+ private CircularBuffer circularBuffer;
+ private readonly WaveFormat waveFormat;
+
+ ///
+ /// Creates a new buffered WaveProvider
+ ///
+ /// WaveFormat
+ public BufferedWaveProvider(WaveFormat waveFormat)
+ {
+ this.waveFormat = waveFormat;
+ this.BufferLength = waveFormat.AverageBytesPerSecond * 5;
+ }
+
+ ///
+ /// Buffer length in bytes
+ ///
+ public int BufferLength { get; set; }
+
+ ///
+ /// Buffer duration
+ ///
+ public TimeSpan BufferDuration
+ {
+ get
+ {
+ return TimeSpan.FromSeconds((double)BufferLength / WaveFormat.AverageBytesPerSecond);
+ }
+ set
+ {
+ BufferLength = (int)(value.TotalSeconds * WaveFormat.AverageBytesPerSecond);
+ }
+ }
+
+ ///
+ /// If true, when the buffer is full, start throwing away data
+ /// if false, AddSamples will throw an exception when buffer is full
+ ///
+ public bool DiscardOnBufferOverflow { get; set; }
+
+ ///
+ /// The number of buffered bytes
+ ///
+ public int BufferedBytes
+ {
+ get
+ {
+ return circularBuffer == null ? 0 : circularBuffer.Count;
+ }
+ }
+
+ ///
+ /// Buffered Duration
+ ///
+ public TimeSpan BufferedDuration
+ {
+ get { return TimeSpan.FromSeconds((double)BufferedBytes / WaveFormat.AverageBytesPerSecond); }
+ }
+
+ ///
+ /// Gets the WaveFormat
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Adds samples. Takes a copy of buffer, so that buffer can be reused if necessary
+ ///
+ public void AddSamples(byte[] buffer, int offset, int count)
+ {
+ // create buffer here to allow user to customise buffer length
+ if (circularBuffer == null)
+ {
+ circularBuffer = new CircularBuffer(this.BufferLength);
+ }
+
+ var written = circularBuffer.Write(buffer, offset, count);
+ if (written < count && !DiscardOnBufferOverflow)
+ {
+ throw new InvalidOperationException("Buffer full");
+ }
+ }
+
+ ///
+ /// Reads from this WaveProvider
+ /// Will always return count bytes, since we will zero-fill the buffer if not enough available
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ int read = 0;
+ if (circularBuffer != null) // not yet created
+ {
+ read = this.circularBuffer.Read(buffer, offset, count);
+ }
+ if (read < count)
+ {
+ // zero the end of the buffer
+ Array.Clear(buffer, offset + read, count - read);
+ }
+ return count;
+ }
+
+ ///
+ /// Discards all audio from the buffer
+ ///
+ public void ClearBuffer()
+ {
+ if (circularBuffer != null)
+ {
+ circularBuffer.Reset();
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/MediaFoundationResampler.cs b/NAudio/Wave/WaveProviders/MediaFoundationResampler.cs
new file mode 100644
index 00000000..374e2bc3
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/MediaFoundationResampler.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Runtime.InteropServices;
+using NAudio.Dmo;
+using NAudio.MediaFoundation;
+
+namespace NAudio.Wave
+{
+ ///
+ /// The Media Foundation Resampler Transform
+ ///
+ public class MediaFoundationResampler : MediaFoundationTransform
+ {
+ private int resamplerQuality;
+
+ private static bool IsPcmOrIeeeFloat(WaveFormat waveFormat)
+ {
+ var wfe = waveFormat as WaveFormatExtensible;
+ return waveFormat.Encoding == WaveFormatEncoding.Pcm ||
+ waveFormat.Encoding == WaveFormatEncoding.IeeeFloat ||
+ (wfe != null && (wfe.SubFormat == AudioSubtypes.MFAudioFormat_PCM
+ || wfe.SubFormat == AudioSubtypes.MFAudioFormat_Float));
+ }
+
+ ///
+ /// Creates the Media Foundation Resampler, allowing modifying of sample rate, bit depth and channel count
+ ///
+ /// Source provider, must be PCM
+ /// Output format, must also be PCM
+ public MediaFoundationResampler(IWaveProvider sourceProvider, WaveFormat outputFormat)
+ : base(sourceProvider, outputFormat)
+ {
+ if (!IsPcmOrIeeeFloat(sourceProvider.WaveFormat))
+ throw new ArgumentException("Input must be PCM or IEEE float", "sourceProvider");
+ if (!IsPcmOrIeeeFloat(outputFormat))
+ throw new ArgumentException("Output must be PCM or IEEE float", "outputFormat");
+ MediaFoundationApi.Startup();
+ ResamplerQuality = 60; // maximum quality
+
+ // n.b. we will create the resampler COM object on demand in the Read method,
+ // to avoid threading issues but just
+ // so we can check it exists on the system we'll make one so it will throw an
+ // exception if not exists
+ var comObject = CreateResamplerComObject();
+ FreeComObject(comObject);
+ }
+
+ private static readonly Guid ResamplerClsid = new Guid("f447b69e-1884-4a7e-8055-346f74d6edb3");
+ private static readonly Guid IMFTransformIid = new Guid("bf94c121-5b05-4e6f-8000-ba598961414d");
+ private IMFActivate activate;
+
+ private void FreeComObject(object comObject)
+ {
+ if (activate != null) activate.ShutdownObject();
+ Marshal.ReleaseComObject(comObject);
+ }
+
+ private object CreateResamplerComObject()
+ {
+#if NETFX_CORE
+ return CreateResamplerComObjectUsingActivator();
+#else
+ return new ResamplerMediaComObject();
+#endif
+ }
+
+ private object CreateResamplerComObjectUsingActivator()
+ {
+ var transformActivators = MediaFoundationApi.EnumerateTransforms(MediaFoundationTransformCategories.AudioEffect);
+ foreach (var activator in transformActivators)
+ {
+ Guid clsid;
+ activator.GetGUID(MediaFoundationAttributes.MFT_TRANSFORM_CLSID_Attribute, out clsid);
+ if (clsid.Equals(ResamplerClsid))
+ {
+ object comObject;
+ activator.ActivateObject(IMFTransformIid, out comObject);
+ activate = activator;
+ return comObject;
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Creates a resampler with a specified target output sample rate
+ ///
+ /// Source provider
+ /// Output sample rate
+ public MediaFoundationResampler(IWaveProvider sourceProvider, int outputSampleRate)
+ : this(sourceProvider, CreateOutputFormat(sourceProvider.WaveFormat, outputSampleRate))
+ {
+
+ }
+
+ ///
+ /// Creates and configures the actual Resampler transform
+ ///
+ /// A newly created and configured resampler MFT
+ protected override IMFTransform CreateTransform()
+ {
+ var comObject = CreateResamplerComObject();// new ResamplerMediaComObject();
+ var resamplerTransform = (IMFTransform)comObject;
+
+ var inputMediaFormat = MediaFoundationApi.CreateMediaTypeFromWaveFormat(sourceProvider.WaveFormat);
+ resamplerTransform.SetInputType(0, inputMediaFormat, 0);
+ Marshal.ReleaseComObject(inputMediaFormat);
+
+ var outputMediaFormat = MediaFoundationApi.CreateMediaTypeFromWaveFormat(outputWaveFormat);
+ resamplerTransform.SetOutputType(0, outputMediaFormat, 0);
+ Marshal.ReleaseComObject(outputMediaFormat);
+
+ //MFT_OUTPUT_STREAM_INFO pStreamInfo;
+ //resamplerTransform.GetOutputStreamInfo(0, out pStreamInfo);
+ // if pStreamInfo.dwFlags is 0, then it means we have to provide samples
+
+ // setup quality
+ var resamplerProps = (IWMResamplerProps)comObject;
+ // 60 is the best quality, 1 is linear interpolation
+ resamplerProps.SetHalfFilterLength(ResamplerQuality);
+ // may also be able to set this using MFPKEY_WMRESAMP_CHANNELMTX on the
+ // IPropertyStore interface.
+ // looks like we can also adjust the LPF with MFPKEY_WMRESAMP_LOWPASS_BANDWIDTH
+ return resamplerTransform;
+ }
+
+ ///
+ /// Gets or sets the Resampler quality. n.b. set the quality before starting to resample.
+ /// 1 is lowest quality (linear interpolation) and 60 is best quality
+ ///
+ public int ResamplerQuality
+ {
+ get { return resamplerQuality; }
+ set
+ {
+ if (value < 1 || value > 60)
+ throw new ArgumentOutOfRangeException("Resampler Quality must be between 1 and 60");
+ resamplerQuality = value;
+ }
+ }
+
+ private static WaveFormat CreateOutputFormat(WaveFormat inputFormat, int outputSampleRate)
+ {
+ WaveFormat outputFormat;
+ if (inputFormat.Encoding == WaveFormatEncoding.Pcm)
+ {
+ outputFormat = new WaveFormat(outputSampleRate,
+ inputFormat.BitsPerSample,
+ inputFormat.Channels);
+ }
+ else if (inputFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ outputFormat = WaveFormat.CreateIeeeFloatWaveFormat(outputSampleRate,
+ inputFormat.Channels);
+ }
+ else
+ {
+ throw new ArgumentException("Can only resample PCM or IEEE float");
+ }
+ return outputFormat;
+ }
+
+ ///
+ /// Disposes this resampler
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (activate != null)
+ {
+ activate.ShutdownObject();
+ activate = null;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/MixingWaveProvider32.cs b/NAudio/Wave/WaveProviders/MixingWaveProvider32.cs
new file mode 100644
index 00000000..aee16e47
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/MixingWaveProvider32.cs
@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveProvider that can mix together multiple 32 bit floating point input provider
+ /// All channels must have the same number of inputs and same sample rate
+ /// n.b. Work in Progress - not tested yet
+ ///
+ public class MixingWaveProvider32 : IWaveProvider
+ {
+ private List inputs;
+ private WaveFormat waveFormat;
+ private int bytesPerSample;
+
+ ///
+ /// Creates a new MixingWaveProvider32
+ ///
+ public MixingWaveProvider32()
+ {
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
+ this.bytesPerSample = 4;
+ this.inputs = new List();
+ }
+
+ ///
+ /// Creates a new 32 bit MixingWaveProvider32
+ ///
+ /// inputs - must all have the same format.
+ /// Thrown if the input streams are not 32 bit floating point,
+ /// or if they have different formats to each other
+ public MixingWaveProvider32(IEnumerable inputs)
+ : this()
+ {
+ foreach (var input in inputs)
+ {
+ AddInputStream(input);
+ }
+ }
+
+ ///
+ /// Add a new input to the mixer
+ ///
+ /// The wave input to add
+ public void AddInputStream(IWaveProvider waveProvider)
+ {
+ if (waveProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ throw new ArgumentException("Must be IEEE floating point", "waveProvider.WaveFormat");
+ if (waveProvider.WaveFormat.BitsPerSample != 32)
+ throw new ArgumentException("Only 32 bit audio currently supported", "waveProvider.WaveFormat");
+
+ if (inputs.Count == 0)
+ {
+ // first one - set the format
+ int sampleRate = waveProvider.WaveFormat.SampleRate;
+ int channels = waveProvider.WaveFormat.Channels;
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
+ }
+ else
+ {
+ if (!waveProvider.WaveFormat.Equals(waveFormat))
+ throw new ArgumentException("All incoming channels must have the same format", "waveProvider.WaveFormat");
+ }
+
+ lock (inputs)
+ {
+ this.inputs.Add(waveProvider);
+ }
+ }
+
+ ///
+ /// Remove an input from the mixer
+ ///
+ /// waveProvider to remove
+ public void RemoveInputStream(IWaveProvider waveProvider)
+ {
+ lock (inputs)
+ {
+ this.inputs.Remove(waveProvider);
+ }
+ }
+
+ ///
+ /// The number of inputs to this mixer
+ ///
+ public int InputCount
+ {
+ get { return this.inputs.Count; }
+ }
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// buffer to read into
+ /// offset into buffer
+ /// number of bytes required
+ /// Number of bytes read.
+ /// Thrown if an invalid number of bytes requested
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ if (count % bytesPerSample != 0)
+ throw new ArgumentException("Must read an whole number of samples", "count");
+
+ // blank the buffer
+ Array.Clear(buffer, offset, count);
+ int bytesRead = 0;
+
+ // sum the channels in
+ byte[] readBuffer = new byte[count];
+ lock (inputs)
+ {
+ foreach (var input in inputs)
+ {
+ int readFromThisStream = input.Read(readBuffer, 0, count);
+ // don't worry if input stream returns less than we requested - may indicate we have got to the end
+ bytesRead = Math.Max(bytesRead, readFromThisStream);
+ if (readFromThisStream > 0)
+ {
+ Sum32BitAudio(buffer, offset, readBuffer, readFromThisStream);
+ }
+ }
+ }
+ return bytesRead;
+ }
+
+ ///
+ /// Actually performs the mixing
+ ///
+ static unsafe void Sum32BitAudio(byte[] destBuffer, int offset, byte[] sourceBuffer, int bytesRead)
+ {
+ fixed (byte* pDestBuffer = &destBuffer[offset],
+ pSourceBuffer = &sourceBuffer[0])
+ {
+ float* pfDestBuffer = (float*)pDestBuffer;
+ float* pfReadBuffer = (float*)pSourceBuffer;
+ int samplesRead = bytesRead / 4;
+ for (int n = 0; n < samplesRead; n++)
+ {
+ pfDestBuffer[n] += pfReadBuffer[n];
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.waveFormat; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/MonoToStereoProvider16.cs b/NAudio/Wave/WaveProviders/MonoToStereoProvider16.cs
new file mode 100644
index 00000000..769790f8
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/MonoToStereoProvider16.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Converts from mono to stereo, allowing freedom to route all, some, or none of the incoming signal to left or right channels
+ ///
+ public class MonoToStereoProvider16 : IWaveProvider
+ {
+ private IWaveProvider sourceProvider;
+ private WaveFormat outputFormat;
+ private byte[] sourceBuffer;
+
+ ///
+ /// Creates a new stereo waveprovider based on a mono input
+ ///
+ /// Mono 16 bit PCM input
+ public MonoToStereoProvider16(IWaveProvider sourceProvider)
+ {
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
+ {
+ throw new ArgumentException("Source must be PCM");
+ }
+ if (sourceProvider.WaveFormat.Channels != 1)
+ {
+ throw new ArgumentException("Source must be Mono");
+ }
+ if (sourceProvider.WaveFormat.BitsPerSample != 16)
+ {
+ throw new ArgumentException("Source must be 16 bit");
+ }
+ this.sourceProvider = sourceProvider;
+ this.outputFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 2);
+ RightVolume = 1.0f;
+ LeftVolume = 1.0f;
+ }
+
+ ///
+ /// 1.0 to copy the mono stream to the left channel without adjusting volume
+ ///
+ public float LeftVolume { get; set; }
+
+ ///
+ /// 1.0 to copy the mono stream to the right channel without adjusting volume
+ ///
+ public float RightVolume { get; set; }
+
+ ///
+ /// Output Wave Format
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.outputFormat; }
+ }
+
+ ///
+ /// Reads bytes from this WaveProvider
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ int sourceBytesRequired = count / 2;
+ this.sourceBuffer = BufferHelpers.Ensure(this.sourceBuffer, sourceBytesRequired);
+ WaveBuffer sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ WaveBuffer destWaveBuffer = new WaveBuffer(buffer);
+
+ int sourceBytesRead = sourceProvider.Read(sourceBuffer, 0, sourceBytesRequired);
+ int samplesRead = sourceBytesRead / 2;
+ int destOffset = offset / 2;
+ for (int sample = 0; sample < samplesRead; sample++)
+ {
+ short sampleVal = sourceWaveBuffer.ShortBuffer[sample];
+ destWaveBuffer.ShortBuffer[destOffset++] = (short)(LeftVolume * sampleVal);
+ destWaveBuffer.ShortBuffer[destOffset++] = (short)(RightVolume * sampleVal);
+ }
+ return samplesRead * 4;
+ }
+
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/MultiplexingWaveProvider.cs b/NAudio/Wave/WaveProviders/MultiplexingWaveProvider.cs
new file mode 100644
index 00000000..5c659c3a
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/MultiplexingWaveProvider.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Allows any number of inputs to be patched to outputs
+ /// Uses could include swapping left and right channels, turning mono into stereo,
+ /// feeding different input sources to different soundcard outputs etc
+ ///
+ public class MultiplexingWaveProvider : IWaveProvider
+ {
+ private readonly IList inputs;
+ private readonly WaveFormat waveFormat;
+ private readonly int outputChannelCount;
+ private readonly int inputChannelCount;
+ private readonly List mappings;
+ private readonly int bytesPerSample;
+
+ ///
+ /// Creates a multiplexing wave provider, allowing re-patching of input channels to different
+ /// output channels
+ ///
+ /// Input wave providers. Must all be of the same format, but can have any number of channels
+ /// Desired number of output channels.
+ public MultiplexingWaveProvider(IEnumerable inputs, int numberOfOutputChannels)
+ {
+ this.inputs = new List(inputs);
+ this.outputChannelCount = numberOfOutputChannels;
+
+ if (this.inputs.Count == 0)
+ {
+ throw new ArgumentException("You must provide at least one input");
+ }
+ if (numberOfOutputChannels < 1)
+ {
+ throw new ArgumentException("You must provide at least one output");
+ }
+ foreach (var input in this.inputs)
+ {
+ if (this.waveFormat == null)
+ {
+ if (input.WaveFormat.Encoding == WaveFormatEncoding.Pcm)
+ {
+ this.waveFormat = new WaveFormat(input.WaveFormat.SampleRate, input.WaveFormat.BitsPerSample, numberOfOutputChannels);
+ }
+ else if (input.WaveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(input.WaveFormat.SampleRate, numberOfOutputChannels);
+ }
+ else
+ {
+ throw new ArgumentException("Only PCM and 32 bit float are supported");
+ }
+ }
+ else
+ {
+ if (input.WaveFormat.BitsPerSample != this.waveFormat.BitsPerSample)
+ {
+ throw new ArgumentException("All inputs must have the same bit depth");
+ }
+ if (input.WaveFormat.SampleRate != this.waveFormat.SampleRate)
+ {
+ throw new ArgumentException("All inputs must have the same sample rate");
+ }
+ }
+ inputChannelCount += input.WaveFormat.Channels;
+ }
+ this.bytesPerSample = this.waveFormat.BitsPerSample / 8;
+
+ mappings = new List();
+ for (int n = 0; n < outputChannelCount; n++)
+ {
+ mappings.Add(n % inputChannelCount);
+ }
+ }
+
+ ///
+ /// persistent temporary buffer to prevent creating work for garbage collector
+ ///
+ private byte[] inputBuffer;
+
+ ///
+ /// Reads data from this WaveProvider
+ ///
+ /// Buffer to be filled with sample data
+ /// Offset to write to within buffer, usually 0
+ /// Number of bytes required
+ /// Number of bytes read
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ int outputBytesPerFrame = bytesPerSample * outputChannelCount;
+ int sampleFramesRequested = count / outputBytesPerFrame;
+ int inputOffset = 0;
+ int sampleFramesRead = 0;
+ // now we must read from all inputs, even if we don't need their data, so they stay in sync
+ foreach (var input in inputs)
+ {
+ int inputBytesPerFrame = bytesPerSample * input.WaveFormat.Channels;
+ int bytesRequired = sampleFramesRequested * inputBytesPerFrame;
+ this.inputBuffer = BufferHelpers.Ensure(this.inputBuffer, bytesRequired);
+ int bytesRead = input.Read(inputBuffer, 0, bytesRequired);
+ sampleFramesRead = Math.Max(sampleFramesRead, bytesRead / inputBytesPerFrame);
+
+ for (int n = 0; n < input.WaveFormat.Channels; n++)
+ {
+ int inputIndex = inputOffset + n;
+ for (int outputIndex = 0; outputIndex < outputChannelCount; outputIndex++)
+ {
+ if (mappings[outputIndex] == inputIndex)
+ {
+ int inputBufferOffset = n * bytesPerSample;
+ int outputBufferOffset = offset + outputIndex * bytesPerSample;
+ int sample = 0;
+ while (sample < sampleFramesRequested && inputBufferOffset < bytesRead)
+ {
+ Array.Copy(inputBuffer, inputBufferOffset, buffer, outputBufferOffset, bytesPerSample);
+ outputBufferOffset += outputBytesPerFrame;
+ inputBufferOffset += inputBytesPerFrame;
+ sample++;
+ }
+ // clear the end
+ while (sample < sampleFramesRequested)
+ {
+ Array.Clear(buffer, outputBufferOffset, bytesPerSample);
+ outputBufferOffset += outputBytesPerFrame;
+ sample++;
+ }
+ }
+ }
+ }
+ inputOffset += input.WaveFormat.Channels;
+ }
+
+ return sampleFramesRead * outputBytesPerFrame;
+ }
+
+ ///
+ /// The WaveFormat of this WaveProvider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Connects a specified input channel to an output channel
+ ///
+ /// Input Channel index (zero based). Must be less than InputChannelCount
+ /// Output Channel index (zero based). Must be less than OutputChannelCount
+ public void ConnectInputToOutput(int inputChannel, int outputChannel)
+ {
+ if (inputChannel < 0 || inputChannel >= InputChannelCount)
+ {
+ throw new ArgumentException("Invalid input channel");
+ }
+ if (outputChannel < 0 || outputChannel >= OutputChannelCount)
+ {
+ throw new ArgumentException("Invalid output channel");
+ }
+ mappings[outputChannel] = inputChannel;
+ }
+
+ ///
+ /// The number of input channels. Note that this is not the same as the number of input wave providers. If you pass in
+ /// one stereo and one mono input provider, the number of input channels is three.
+ ///
+ public int InputChannelCount
+ {
+ get { return inputChannelCount; }
+ }
+
+ ///
+ /// The number of output channels, as specified in the constructor.
+ ///
+ public int OutputChannelCount
+ {
+ get { return outputChannelCount; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/StereoToMonoProvider16.cs b/NAudio/Wave/WaveProviders/StereoToMonoProvider16.cs
new file mode 100644
index 00000000..8fc83389
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/StereoToMonoProvider16.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Takes a stereo 16 bit input and turns it mono, allowing you to select left or right channel only or mix them together
+ ///
+ public class StereoToMonoProvider16 : IWaveProvider
+ {
+ private IWaveProvider sourceProvider;
+ private WaveFormat outputFormat;
+ private byte[] sourceBuffer;
+
+ ///
+ /// Creates a new mono waveprovider based on a stereo input
+ ///
+ /// Stereo 16 bit PCM input
+ public StereoToMonoProvider16(IWaveProvider sourceProvider)
+ {
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
+ {
+ throw new ArgumentException("Source must be PCM");
+ }
+ if (sourceProvider.WaveFormat.Channels != 2)
+ {
+ throw new ArgumentException("Source must be stereo");
+ }
+ if (sourceProvider.WaveFormat.BitsPerSample != 16)
+ {
+ throw new ArgumentException("Source must be 16 bit");
+ }
+ this.sourceProvider = sourceProvider;
+ this.outputFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 1);
+ }
+
+ ///
+ /// 1.0 to mix the mono source entirely to the left channel
+ ///
+ public float LeftVolume { get; set; }
+
+ ///
+ /// 1.0 to mix the mono source entirely to the right channel
+ ///
+ public float RightVolume { get; set; }
+
+ ///
+ /// Output Wave Format
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return this.outputFormat; }
+ }
+
+ ///
+ /// Reads bytes from this WaveProvider
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ int sourceBytesRequired = count * 2;
+ this.sourceBuffer = BufferHelpers.Ensure(this.sourceBuffer, sourceBytesRequired);
+ WaveBuffer sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ WaveBuffer destWaveBuffer = new WaveBuffer(buffer);
+
+ int sourceBytesRead = sourceProvider.Read(sourceBuffer, 0, sourceBytesRequired);
+ int samplesRead = sourceBytesRead / 2;
+ int destOffset = offset / 2;
+ for (int sample = 0; sample < samplesRead; sample+=2)
+ {
+ short left = sourceWaveBuffer.ShortBuffer[sample];
+ short right = sourceWaveBuffer.ShortBuffer[sample+1];
+ float outSample = (left * LeftVolume) + (right * RightVolume);
+ // hard limiting
+ if (outSample > Int16.MaxValue) outSample = Int16.MaxValue;
+ if (outSample < Int16.MinValue) outSample = Int16.MinValue;
+
+ destWaveBuffer.ShortBuffer[destOffset++] = (short)outSample;
+ }
+ return sourceBytesRead / 2;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/VolumeWaveProvider16.cs b/NAudio/Wave/WaveProviders/VolumeWaveProvider16.cs
new file mode 100644
index 00000000..6d81f00c
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/VolumeWaveProvider16.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Helper class allowing us to modify the volume of a 16 bit stream without converting to IEEE float
+ ///
+ public class VolumeWaveProvider16 : IWaveProvider
+ {
+ private readonly IWaveProvider sourceProvider;
+ private float volume;
+
+ ///
+ /// Constructs a new VolumeWaveProvider16
+ ///
+ /// Source provider, must be 16 bit PCM
+ public VolumeWaveProvider16(IWaveProvider sourceProvider)
+ {
+ this.Volume = 1.0f;
+ this.sourceProvider = sourceProvider;
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
+ throw new ArgumentException("Expecting PCM input");
+ if (sourceProvider.WaveFormat.BitsPerSample != 16)
+ throw new ArgumentException("Expecting 16 bit");
+ }
+
+ ///
+ /// Gets or sets volume.
+ /// 1.0 is full scale, 0.0 is silence, anything over 1.0 will amplify but potentially clip
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+
+ ///
+ /// WaveFormat of this WaveProvider
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return sourceProvider.WaveFormat; }
+ }
+
+ ///
+ /// Read bytes from this WaveProvider
+ ///
+ /// Buffer to read into
+ /// Offset within buffer to read to
+ /// Bytes desired
+ /// Bytes read
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ // always read from the source
+ int bytesRead = sourceProvider.Read(buffer, offset, count);
+ if (this.volume == 0.0f)
+ {
+ for (int n = 0; n < bytesRead; n++)
+ {
+ buffer[offset++] = 0;
+ }
+ }
+ else if (this.volume != 1.0f)
+ {
+ for (int n = 0; n < bytesRead; n += 2)
+ {
+ short sample = (short)((buffer[offset + 1] << 8) | buffer[offset]);
+ var newSample = sample * this.volume;
+ sample = (short)newSample;
+ // clip if necessary
+ if (this.Volume > 1.0f)
+ {
+ if (newSample > Int16.MaxValue) sample = Int16.MaxValue;
+ else if (newSample < Int16.MinValue) sample = Int16.MinValue;
+ }
+
+ buffer[offset++] = (byte)(sample & 0xFF);
+ buffer[offset++] = (byte)(sample >> 8);
+ }
+ }
+ return bytesRead;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/Wave16toFloatProvider.cs b/NAudio/Wave/WaveProviders/Wave16toFloatProvider.cs
new file mode 100644
index 00000000..3392100d
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/Wave16toFloatProvider.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Converts 16 bit PCM to IEEE float, optionally adjusting volume along the way
+ ///
+ public class Wave16ToFloatProvider : IWaveProvider
+ {
+ private IWaveProvider sourceProvider;
+ private readonly WaveFormat waveFormat;
+ private volatile float volume;
+ private byte[] sourceBuffer;
+
+ ///
+ /// Creates a new Wave16toFloatProvider
+ ///
+ /// the source provider
+ public Wave16ToFloatProvider(IWaveProvider sourceProvider)
+ {
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
+ throw new ArgumentException("Only PCM supported");
+ if (sourceProvider.WaveFormat.BitsPerSample != 16)
+ throw new ArgumentException("Only 16 bit audio supported");
+
+ waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sourceProvider.WaveFormat.SampleRate, sourceProvider.WaveFormat.Channels);
+
+ this.sourceProvider = sourceProvider;
+ this.volume = 1.0f;
+ }
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// The destination buffer
+ /// Offset into the destination buffer
+ /// Number of bytes read
+ /// Number of bytes read.
+ public int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ int sourceBytesRequired = numBytes / 2;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ int sourceBytesRead = sourceProvider.Read(sourceBuffer, offset, sourceBytesRequired);
+ WaveBuffer sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ WaveBuffer destWaveBuffer = new WaveBuffer(destBuffer);
+
+ int sourceSamples = sourceBytesRead / 2;
+ int destOffset = offset / 4;
+ for (int sample = 0; sample < sourceSamples; sample++)
+ {
+ destWaveBuffer.FloatBuffer[destOffset++] = (sourceWaveBuffer.ShortBuffer[sample] / 32768f) * volume;
+ }
+
+ return sourceSamples * 4;
+ }
+
+ ///
+ ///
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Volume of this channel. 1.0 = full scale
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/WaveFloatTo16Provider.cs b/NAudio/Wave/WaveProviders/WaveFloatTo16Provider.cs
new file mode 100644
index 00000000..340aaddb
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/WaveFloatTo16Provider.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Converts IEEE float to 16 bit PCM, optionally clipping and adjusting volume along the way
+ ///
+ public class WaveFloatTo16Provider : IWaveProvider
+ {
+ private readonly IWaveProvider sourceProvider;
+ private readonly WaveFormat waveFormat;
+ private volatile float volume;
+ private byte[] sourceBuffer;
+
+ ///
+ /// Creates a new WaveFloatTo16Provider
+ ///
+ /// the source provider
+ public WaveFloatTo16Provider(IWaveProvider sourceProvider)
+ {
+ if (sourceProvider.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ throw new ArgumentException("Input wave provider must be IEEE float", "sourceProvider");
+ if (sourceProvider.WaveFormat.BitsPerSample != 32)
+ throw new ArgumentException("Input wave provider must be 32 bit", "sourceProvider");
+
+ waveFormat = new WaveFormat(sourceProvider.WaveFormat.SampleRate, 16, sourceProvider.WaveFormat.Channels);
+
+ this.sourceProvider = sourceProvider;
+ this.volume = 1.0f;
+ }
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// The destination buffer
+ /// Offset into the destination buffer
+ /// Number of bytes read
+ /// Number of bytes read.
+ public int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ int sourceBytesRequired = numBytes * 2;
+ sourceBuffer = BufferHelpers.Ensure(sourceBuffer, sourceBytesRequired);
+ int sourceBytesRead = sourceProvider.Read(sourceBuffer, 0, sourceBytesRequired);
+ WaveBuffer sourceWaveBuffer = new WaveBuffer(sourceBuffer);
+ WaveBuffer destWaveBuffer = new WaveBuffer(destBuffer);
+
+ int sourceSamples = sourceBytesRead / 4;
+ int destOffset = offset / 2;
+ for (int sample = 0; sample < sourceSamples; sample++)
+ {
+ // adjust volume
+ float sample32 = sourceWaveBuffer.FloatBuffer[sample] * volume;
+ // clip
+ if (sample32 > 1.0f)
+ sample32 = 1.0f;
+ if (sample32 < -1.0f)
+ sample32 = -1.0f;
+ destWaveBuffer.ShortBuffer[destOffset++] = (short)(sample32 * 32767);
+ }
+
+ return sourceSamples * 2;
+ }
+
+ ///
+ ///
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// Volume of this channel. 1.0 = full scale
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/WaveInProvider.cs b/NAudio/Wave/WaveProviders/WaveInProvider.cs
new file mode 100644
index 00000000..71b3f32b
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/WaveInProvider.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Buffered WaveProvider taking source data from WaveIn
+ ///
+ public class WaveInProvider : IWaveProvider
+ {
+ IWaveIn waveIn;
+ BufferedWaveProvider bufferedWaveProvider;
+
+ ///
+ /// Creates a new WaveInProvider
+ /// n.b. Should make sure the WaveFormat is set correctly on IWaveIn before calling
+ ///
+ /// The source of wave data
+ public WaveInProvider(IWaveIn waveIn)
+ {
+ this.waveIn = waveIn;
+ waveIn.DataAvailable += waveIn_DataAvailable;
+ bufferedWaveProvider = new BufferedWaveProvider(this.WaveFormat);
+ }
+
+ void waveIn_DataAvailable(object sender, WaveInEventArgs e)
+ {
+ bufferedWaveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
+ }
+
+ ///
+ /// Reads data from the WaveInProvider
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ return bufferedWaveProvider.Read(buffer, 0, count);
+ }
+
+ ///
+ /// The WaveFormat
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveIn.WaveFormat; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/WaveProvider16.cs b/NAudio/Wave/WaveProviders/WaveProvider16.cs
new file mode 100644
index 00000000..fdff5c83
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/WaveProvider16.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Base class for creating a 16 bit wave provider
+ ///
+ public abstract class WaveProvider16 : IWaveProvider
+ {
+ private WaveFormat waveFormat;
+
+ ///
+ /// Initializes a new instance of the WaveProvider16 class
+ /// defaulting to 44.1kHz mono
+ ///
+ public WaveProvider16()
+ : this(44100, 1)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the WaveProvider16 class with the specified
+ /// sample rate and number of channels
+ ///
+ public WaveProvider16(int sampleRate, int channels)
+ {
+ SetWaveFormat(sampleRate, channels);
+ }
+
+ ///
+ /// Allows you to specify the sample rate and channels for this WaveProvider
+ /// (should be initialised before you pass it to a wave player)
+ ///
+ public void SetWaveFormat(int sampleRate, int channels)
+ {
+ this.waveFormat = new WaveFormat(sampleRate, 16, channels);
+ }
+
+ ///
+ /// Implements the Read method of IWaveProvider by delegating to the abstract
+ /// Read method taking a short array
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ WaveBuffer waveBuffer = new WaveBuffer(buffer);
+ int samplesRequired = count / 2;
+ int samplesRead = Read(waveBuffer.ShortBuffer, offset / 2, samplesRequired);
+ return samplesRead * 2;
+ }
+
+ ///
+ /// Method to override in derived classes
+ /// Supply the requested number of samples into the buffer
+ ///
+ public abstract int Read(short[] buffer, int offset, int sampleCount);
+
+ ///
+ /// The Wave Format
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/WaveProvider32.cs b/NAudio/Wave/WaveProviders/WaveProvider32.cs
new file mode 100644
index 00000000..f3ad42c3
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/WaveProvider32.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Base class for creating a 32 bit floating point wave provider
+ /// Can also be used as a base class for an ISampleProvider that can
+ /// be plugged straight into anything requiring an IWaveProvider
+ ///
+ public abstract class WaveProvider32 : IWaveProvider, ISampleProvider
+ {
+ private WaveFormat waveFormat;
+
+ ///
+ /// Initializes a new instance of the WaveProvider32 class
+ /// defaulting to 44.1kHz mono
+ ///
+ public WaveProvider32()
+ : this(44100, 1)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the WaveProvider32 class with the specified
+ /// sample rate and number of channels
+ ///
+ public WaveProvider32(int sampleRate, int channels)
+ {
+ SetWaveFormat(sampleRate, channels);
+ }
+
+ ///
+ /// Allows you to specify the sample rate and channels for this WaveProvider
+ /// (should be initialised before you pass it to a wave player)
+ ///
+ public void SetWaveFormat(int sampleRate, int channels)
+ {
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
+ }
+
+ ///
+ /// Implements the Read method of IWaveProvider by delegating to the abstract
+ /// Read method taking a float array
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ WaveBuffer waveBuffer = new WaveBuffer(buffer);
+ int samplesRequired = count / 4;
+ int samplesRead = Read(waveBuffer.FloatBuffer, offset / 4, samplesRequired);
+ return samplesRead * 4;
+ }
+
+ ///
+ /// Method to override in derived classes
+ /// Supply the requested number of samples into the buffer
+ ///
+ public abstract int Read(float[] buffer, int offset, int sampleCount);
+
+ ///
+ /// The Wave Format
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveProviders/WaveRecorder.cs b/NAudio/Wave/WaveProviders/WaveRecorder.cs
new file mode 100644
index 00000000..7f754f14
--- /dev/null
+++ b/NAudio/Wave/WaveProviders/WaveRecorder.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Utility class to intercept audio from an IWaveProvider and
+ /// save it to disk
+ ///
+ public class WaveRecorder : IWaveProvider, IDisposable
+ {
+ private WaveFileWriter writer;
+ private IWaveProvider source;
+
+ ///
+ /// Constructs a new WaveRecorder
+ ///
+ /// The location to write the WAV file to
+ /// The Source Wave Provider
+ public WaveRecorder(IWaveProvider source, string destination)
+ {
+ this.source = source;
+ this.writer = new WaveFileWriter(destination, source.WaveFormat);
+ }
+
+ ///
+ /// Read simply returns what the source returns, but writes to disk along the way
+ ///
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ int bytesRead = source.Read(buffer, offset, count);
+ writer.Write(buffer, offset, bytesRead);
+ return bytesRead;
+ }
+
+ ///
+ /// The WaveFormat
+ ///
+ public WaveFormat WaveFormat
+ {
+ get { return source.WaveFormat; }
+ }
+
+ ///
+ /// Closes the WAV file
+ ///
+ public void Dispose()
+ {
+ if (writer != null)
+ {
+ writer.Dispose();
+ writer = null;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/AiffFileReader.cs b/NAudio/Wave/WaveStreams/AiffFileReader.cs
new file mode 100644
index 00000000..5a5dfc3c
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/AiffFileReader.cs
@@ -0,0 +1,330 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Diagnostics;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ /// A read-only stream of AIFF data based on an aiff file
+ /// with an associated WaveFormat
+ /// originally contributed to NAudio by Giawa
+ ///
+ public class AiffFileReader : WaveStream
+ {
+ private readonly WaveFormat waveFormat;
+ private readonly bool ownInput;
+ private readonly long dataPosition;
+ private readonly int dataChunkLength;
+ private readonly List chunks = new List();
+ private Stream waveStream;
+ private readonly object lockObject = new object();
+
+ /// Supports opening a AIF file
+ /// The AIF is of similar nastiness to the WAV format.
+ /// This supports basic reading of uncompressed PCM AIF files,
+ /// with 8, 16, 24 and 32 bit PCM data.
+ ///
+ public AiffFileReader(String aiffFile) :
+ this(File.OpenRead(aiffFile))
+ {
+ ownInput = true;
+ }
+
+ ///
+ /// Creates an Aiff File Reader based on an input stream
+ ///
+ /// The input stream containing a AIF file including header
+ public AiffFileReader(Stream inputStream)
+ {
+ this.waveStream = inputStream;
+ ReadAiffHeader(waveStream, out waveFormat, out dataPosition, out dataChunkLength, chunks);
+ Position = 0;
+ }
+
+ ///
+ /// Ensures valid AIFF header and then finds data offset.
+ ///
+ /// The stream, positioned at the start of audio data
+ /// The format found
+ /// The position of the data chunk
+ /// The length of the data chunk
+ /// Additional chunks found
+ public static void ReadAiffHeader(Stream stream, out WaveFormat format, out long dataChunkPosition, out int dataChunkLength, List chunks)
+ {
+ dataChunkPosition = -1;
+ format = null;
+ BinaryReader br = new BinaryReader(stream);
+
+ if (ReadChunkName(br) != "FORM")
+ {
+ throw new FormatException("Not an AIFF file - no FORM header.");
+ }
+ uint fileSize = ConvertInt(br.ReadBytes(4));
+ string formType = ReadChunkName(br);
+ if (formType != "AIFC" && formType != "AIFF")
+ {
+ throw new FormatException("Not an AIFF file - no AIFF/AIFC header.");
+ }
+
+ dataChunkLength = 0;
+
+ while (br.BaseStream.Position < br.BaseStream.Length)
+ {
+ AiffChunk nextChunk = ReadChunkHeader(br);
+ if (nextChunk.ChunkName == "COMM")
+ {
+ short numChannels = ConvertShort(br.ReadBytes(2));
+ uint numSampleFrames = ConvertInt(br.ReadBytes(4));
+ short sampleSize = ConvertShort(br.ReadBytes(2));
+ double sampleRate = IEEE.ConvertFromIeeeExtended(br.ReadBytes(10));
+
+ format = new WaveFormat((int)sampleRate, (int)sampleSize, (int)numChannels);
+
+ if (nextChunk.ChunkLength > 18 && formType == "AIFC")
+ {
+ // In an AIFC file, the compression format is tacked on to the COMM chunk
+ string compress = new string(br.ReadChars(4)).ToLower();
+ if (compress != "none") throw new FormatException("Compressed AIFC is not supported.");
+ br.ReadBytes((int)nextChunk.ChunkLength - 22);
+ }
+ else br.ReadBytes((int)nextChunk.ChunkLength - 18);
+ }
+ else if (nextChunk.ChunkName == "SSND")
+ {
+ uint offset = ConvertInt(br.ReadBytes(4));
+ uint blockSize = ConvertInt(br.ReadBytes(4));
+ dataChunkPosition = nextChunk.ChunkStart + 16 + offset;
+ dataChunkLength = (int)nextChunk.ChunkLength - 8;
+
+ br.ReadBytes((int)nextChunk.ChunkLength - 8);
+ }
+ else
+ {
+ if (chunks != null)
+ {
+ chunks.Add(nextChunk);
+ }
+ br.ReadBytes((int)nextChunk.ChunkLength);
+ }
+
+ if (nextChunk.ChunkName == "\0\0\0\0") break;
+ }
+
+ if (format == null)
+ {
+ throw new FormatException("Invalid AIFF file - No COMM chunk found.");
+ }
+ if (dataChunkPosition == -1)
+ {
+ throw new FormatException("Invalid AIFF file - No SSND chunk found.");
+ }
+ }
+
+ ///
+ /// Cleans up the resources associated with this AiffFileReader
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Release managed resources.
+ if (waveStream != null)
+ {
+ // only dispose our source if we created it
+ if (ownInput)
+ {
+ waveStream.Close();
+ }
+ waveStream = null;
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "AiffFileReader was not disposed");
+ }
+ // Release unmanaged resources.
+ // Set large fields to null.
+ // Call Dispose on your base class.
+ base.Dispose(disposing);
+ }
+
+ ///
+ ///
+ ///
+ public override NAudio.Wave.WaveFormat WaveFormat
+ {
+ get
+ {
+ return waveFormat;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override long Length
+ {
+ get
+ {
+ return dataChunkLength;
+ }
+ }
+
+ ///
+ /// Number of Samples (if possible to calculate)
+ ///
+ public long SampleCount
+ {
+ get
+ {
+ if (waveFormat.Encoding == WaveFormatEncoding.Pcm ||
+ waveFormat.Encoding == WaveFormatEncoding.Extensible ||
+ waveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ return dataChunkLength / BlockAlign;
+ }
+ else
+ {
+ throw new FormatException("Sample count is calculated only for the standard encodings");
+ }
+ }
+ }
+
+ ///
+ /// Position in the AIFF file
+ ///
+ ///
+ public override long Position
+ {
+ get
+ {
+ return waveStream.Position - dataPosition;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ value = Math.Min(value, Length);
+ // make sure we don't get out of sync
+ value -= (value % waveFormat.BlockAlign);
+ waveStream.Position = value + dataPosition;
+ }
+ }
+ }
+
+
+ ///
+ /// Reads bytes from the AIFF File
+ ///
+ ///
+ public override int Read(byte[] array, int offset, int count)
+ {
+ if (count % waveFormat.BlockAlign != 0)
+ {
+ throw new ArgumentException(String.Format("Must read complete blocks: requested {0}, block align is {1}", count, this.WaveFormat.BlockAlign));
+ }
+ lock (lockObject)
+ {
+ // sometimes there is more junk at the end of the file past the data chunk
+ if (Position + count > dataChunkLength)
+ {
+ count = dataChunkLength - (int) Position;
+ }
+
+ // Need to fix the endianness since intel expect little endian, and apple is big endian.
+ byte[] buffer = new byte[count];
+ int length = waveStream.Read(buffer, offset, count);
+
+ int bytesPerSample = WaveFormat.BitsPerSample/8;
+ for (int i = 0; i < length; i += bytesPerSample)
+ {
+ if (WaveFormat.BitsPerSample == 8)
+ {
+ array[i] = buffer[i];
+ }
+ else if (WaveFormat.BitsPerSample == 16)
+ {
+ array[i + 0] = buffer[i + 1];
+ array[i + 1] = buffer[i];
+ }
+ else if (WaveFormat.BitsPerSample == 24)
+ {
+ array[i + 0] = buffer[i + 2];
+ array[i + 1] = buffer[i + 1];
+ array[i + 2] = buffer[i + 0];
+ }
+ else if (WaveFormat.BitsPerSample == 32)
+ {
+ array[i + 0] = buffer[i + 3];
+ array[i + 1] = buffer[i + 2];
+ array[i + 2] = buffer[i + 1];
+ array[i + 3] = buffer[i + 0];
+ }
+ else throw new FormatException("Unsupported PCM format.");
+ }
+
+ return length;
+ }
+ }
+
+ #region Endian Helpers
+ private static uint ConvertInt(byte[] buffer)
+ {
+ if (buffer.Length != 4) throw new Exception("Incorrect length for long.");
+ return (uint)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]);
+ }
+
+ private static short ConvertShort(byte[] buffer)
+ {
+ if (buffer.Length != 2) throw new Exception("Incorrect length for int.");
+ return (short)((buffer[0] << 8) | buffer[1]);
+ }
+ #endregion
+
+
+ #region AiffChunk
+ ///
+ /// AIFF Chunk
+ ///
+ public struct AiffChunk
+ {
+ ///
+ /// Chunk Name
+ ///
+ public string ChunkName;
+
+ ///
+ /// Chunk Length
+ ///
+ public uint ChunkLength;
+
+ ///
+ /// Chunk start
+ ///
+ public uint ChunkStart;
+
+ ///
+ /// Creates a new AIFF Chunk
+ ///
+ public AiffChunk(uint start, string name, uint length)
+ {
+ ChunkStart = start;
+ ChunkName = name;
+ ChunkLength = length + (uint)(length % 2 == 1 ? 1 : 0);
+ }
+ }
+
+ private static AiffChunk ReadChunkHeader(BinaryReader br)
+ {
+ var chunk = new AiffChunk((uint)br.BaseStream.Position, ReadChunkName(br), ConvertInt(br.ReadBytes(4)));
+ return chunk;
+ }
+
+ private static string ReadChunkName(BinaryReader br)
+ {
+ return new string(br.ReadChars(4));
+ }
+ #endregion
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/AudioFileReader.cs b/NAudio/Wave/WaveStreams/AudioFileReader.cs
new file mode 100644
index 00000000..93b3f833
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/AudioFileReader.cs
@@ -0,0 +1,165 @@
+using System;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudio.Wave
+{
+ ///
+ /// AudioFileReader simplifies opening an audio file in NAudio
+ /// Simply pass in the filename, and it will attempt to open the
+ /// file and set up a conversion path that turns into PCM IEEE float.
+ /// ACM codecs will be used for conversion.
+ /// It provides a volume property and implements both WaveStream and
+ /// ISampleProvider, making it possibly the only stage in your audio
+ /// pipeline necessary for simple playback scenarios
+ ///
+ public class AudioFileReader : WaveStream, ISampleProvider
+ {
+ private string fileName;
+ private WaveStream readerStream; // the waveStream which we will use for all positioning
+ private readonly SampleChannel sampleChannel; // sample provider that gives us most stuff we need
+ private readonly int destBytesPerSample;
+ private readonly int sourceBytesPerSample;
+ private readonly long length;
+ private readonly object lockObject;
+
+ ///
+ /// Initializes a new instance of AudioFileReader
+ ///
+ /// The file to open
+ public AudioFileReader(string fileName)
+ {
+ lockObject = new object();
+ this.fileName = fileName;
+ CreateReaderStream(fileName);
+ sourceBytesPerSample = (readerStream.WaveFormat.BitsPerSample / 8) * readerStream.WaveFormat.Channels;
+ sampleChannel = new SampleChannel(readerStream, false);
+ destBytesPerSample = 4*sampleChannel.WaveFormat.Channels;
+ length = SourceToDest(readerStream.Length);
+ }
+
+ ///
+ /// Creates the reader stream, supporting all filetypes in the core NAudio library,
+ /// and ensuring we are in PCM format
+ ///
+ /// File Name
+ private void CreateReaderStream(string fileName)
+ {
+ if (fileName.EndsWith(".wav", StringComparison.OrdinalIgnoreCase))
+ {
+ readerStream = new WaveFileReader(fileName);
+ if (readerStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm && readerStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ {
+ readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
+ readerStream = new BlockAlignReductionStream(readerStream);
+ }
+ }
+ else if (fileName.EndsWith(".mp3", StringComparison.OrdinalIgnoreCase))
+ {
+ readerStream = new Mp3FileReader(fileName);
+ }
+ else if (fileName.EndsWith(".aiff"))
+ {
+ readerStream = new AiffFileReader(fileName);
+ }
+ else
+ {
+ // fall back to media foundation reader, see if that can play it
+ readerStream = new MediaFoundationReader(fileName);
+ }
+ }
+
+ ///
+ /// WaveFormat of this stream
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return sampleChannel.WaveFormat; }
+ }
+
+ ///
+ /// Length of this stream (in bytes)
+ ///
+ public override long Length
+ {
+ get { return length; }
+ }
+
+ ///
+ /// Position of this stream (in bytes)
+ ///
+ public override long Position
+ {
+ get { return SourceToDest(readerStream.Position); }
+ set { lock (lockObject) { readerStream.Position = DestToSource(value); } }
+ }
+
+ ///
+ /// Reads from this wave stream
+ ///
+ /// Audio buffer
+ /// Offset into buffer
+ /// Number of bytes required
+ /// Number of bytes read
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ var waveBuffer = new WaveBuffer(buffer);
+ int samplesRequired = count / 4;
+ int samplesRead = Read(waveBuffer.FloatBuffer, offset / 4, samplesRequired);
+ return samplesRead * 4;
+ }
+
+ ///
+ /// Reads audio from this sample provider
+ ///
+ /// Sample buffer
+ /// Offset into sample buffer
+ /// Number of samples required
+ /// Number of samples read
+ public int Read(float[] buffer, int offset, int count)
+ {
+ lock (lockObject)
+ {
+ return sampleChannel.Read(buffer, offset, count);
+ }
+ }
+
+ ///
+ /// Gets or Sets the Volume of this AudioFileReader. 1.0f is full volume
+ ///
+ public float Volume
+ {
+ get { return sampleChannel.Volume; }
+ set { sampleChannel.Volume = value; }
+ }
+
+ ///
+ /// Helper to convert source to dest bytes
+ ///
+ private long SourceToDest(long sourceBytes)
+ {
+ return destBytesPerSample * (sourceBytes / sourceBytesPerSample);
+ }
+
+ ///
+ /// Helper to convert dest to source bytes
+ ///
+ private long DestToSource(long destBytes)
+ {
+ return sourceBytesPerSample * (destBytes / destBytesPerSample);
+ }
+
+ ///
+ /// Disposes this AudioFileReader
+ ///
+ /// True if called from Dispose
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ readerStream.Dispose();
+ readerStream = null;
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/BlockAlignReductionStream.cs b/NAudio/Wave/WaveStreams/BlockAlignReductionStream.cs
new file mode 100644
index 00000000..da91a785
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/BlockAlignReductionStream.cs
@@ -0,0 +1,178 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Helper stream that lets us read from compressed audio files with large block alignment
+ /// as though we could read any amount and reposition anywhere
+ ///
+ public class BlockAlignReductionStream : WaveStream
+ {
+ private WaveStream sourceStream;
+ private long position;
+ private readonly CircularBuffer circularBuffer;
+ private long bufferStartPosition;
+ private byte[] sourceBuffer;
+ private readonly object lockObject = new object();
+
+ ///
+ /// Creates a new BlockAlignReductionStream
+ ///
+ /// the input stream
+ public BlockAlignReductionStream(WaveStream sourceStream)
+ {
+ this.sourceStream = sourceStream;
+ circularBuffer = new CircularBuffer(sourceStream.WaveFormat.AverageBytesPerSecond * 4);
+ }
+
+ private byte[] GetSourceBuffer(int size)
+ {
+ if (sourceBuffer == null || sourceBuffer.Length < size)
+ {
+ // let's give ourselves some leeway
+ sourceBuffer = new byte[size * 2];
+ }
+ return sourceBuffer;
+ }
+
+ ///
+ /// Block alignment of this stream
+ ///
+ public override int BlockAlign
+ {
+ get
+ {
+ // can position to sample level
+ return (WaveFormat.BitsPerSample / 8) * WaveFormat.Channels;
+ }
+ }
+
+ ///
+ /// Wave Format
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return sourceStream.WaveFormat; }
+ }
+
+ ///
+ /// Length of this Stream
+ ///
+ public override long Length
+ {
+ get { return sourceStream.Length; }
+ }
+
+ ///
+ /// Current position within stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ if (position != value)
+ {
+ if (position % BlockAlign != 0)
+ throw new ArgumentException("Position must be block aligned");
+ long sourcePosition = value - (value % sourceStream.BlockAlign);
+ if (sourceStream.Position != sourcePosition)
+ {
+ sourceStream.Position = sourcePosition;
+ circularBuffer.Reset();
+ bufferStartPosition = sourceStream.Position;
+ }
+ position = value;
+ }
+ }
+ }
+ }
+
+ private long BufferEndPosition
+ {
+ get
+ {
+
+ return bufferStartPosition + circularBuffer.Count;
+ }
+ }
+
+ ///
+ /// Disposes this WaveStream
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (sourceStream != null)
+ {
+ sourceStream.Dispose();
+ sourceStream = null;
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "BlockAlignReductionStream was not Disposed");
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// Reads data from this stream
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ lock (lockObject)
+ {
+ // 1. attempt to fill the circular buffer with enough data to meet our request
+ while (BufferEndPosition < position + count)
+ {
+ int sourceReadCount = count;
+ if (sourceReadCount % sourceStream.BlockAlign != 0)
+ {
+ sourceReadCount = (count + sourceStream.BlockAlign) - (count % sourceStream.BlockAlign);
+ }
+
+ int sourceRead = sourceStream.Read(GetSourceBuffer(sourceReadCount), 0, sourceReadCount);
+ circularBuffer.Write(GetSourceBuffer(sourceReadCount), 0, sourceRead);
+ if (sourceRead == 0)
+ {
+ // assume we have run out of data
+ break;
+ }
+ }
+
+ // 2. discard any unnecessary stuff from the start
+ if (bufferStartPosition < position)
+ {
+ circularBuffer.Advance((int)(position - bufferStartPosition));
+ bufferStartPosition = position;
+ }
+
+ // 3. now whatever is in the buffer we can return
+ int bytesRead = circularBuffer.Read(buffer, offset, count);
+ position += bytesRead;
+ // anything left in buffer is at start position
+ bufferStartPosition = position;
+
+ return bytesRead;
+ }
+ }
+ }
+
+
+
+}
diff --git a/NAudio/Wave/WaveStreams/CueList.cs b/NAudio/Wave/WaveStreams/CueList.cs
new file mode 100644
index 00000000..0854d422
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/CueList.cs
@@ -0,0 +1,299 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using NAudio.Utils;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Holds information on a cue: a labeled position within a Wave file
+ ///
+ public class Cue
+ {
+ ///
+ /// Cue position in samples
+ ///
+ public int Position
+ {
+ get;
+ private set;
+ }
+ ///
+ /// Label of the cue
+ ///
+ public string Label
+ {
+ get;
+ private set;
+ }
+ ///
+ /// Creates a Cue based on a sample position and label
+ ///
+ ///
+ ///
+ public Cue(int position, string label)
+ {
+ Position = position;
+ if (label == null)
+ {
+ label = "";
+ }
+ Label = Regex.Replace(label, @"[^\u0000-\u00FF]", "");
+ }
+ }
+
+ ///
+ /// Holds a list of cues
+ ///
+ ///
+ /// The specs for reading and writing cues from the cue and list RIFF chunks
+ /// are from http://www.sonicspot.com/guide/wavefiles.html and http://www.wotsit.org/
+ /// ------------------------------
+ /// The cues are stored like this:
+ /// ------------------------------
+ /// struct CuePoint
+ /// {
+ /// Int32 dwIdentifier;
+ /// Int32 dwPosition;
+ /// Int32 fccChunk;
+ /// Int32 dwChunkStart;
+ /// Int32 dwBlockStart;
+ /// Int32 dwSampleOffset;
+ /// }
+ ///
+ /// struct CueChunk
+ /// {
+ /// Int32 chunkID;
+ /// Int32 chunkSize;
+ /// Int32 dwCuePoints;
+ /// CuePoint[] points;
+ /// }
+ /// ------------------------------
+ /// Labels look like this:
+ /// ------------------------------
+ /// struct ListHeader
+ /// {
+ /// Int32 listID; /* 'list' */
+ /// Int32 chunkSize; /* includes the Type ID below */
+ /// Int32 typeID; /* 'adtl' */
+ /// }
+ ///
+ /// struct LabelChunk
+ /// {
+ /// Int32 chunkID;
+ /// Int32 chunkSize;
+ /// Int32 dwIdentifier;
+ /// Char[] dwText; /* Encoded with extended ASCII */
+ /// } LabelChunk;
+ ///
+ public class CueList
+ {
+ private readonly List cues = new List();
+ ///
+ /// Creates an empty cue list
+ ///
+ public CueList()
+ {
+
+ }
+
+ ///
+ /// Adds an item to the list
+ ///
+ /// Cue
+ public void Add(Cue cue)
+ {
+ cues.Add(cue);
+ }
+
+ ///
+ /// Gets sample positions for the embedded cues
+ ///
+ /// Array containing the cue positions
+ public int[] CuePositions
+ {
+ get
+ {
+ int[] positions = new int[cues.Count];
+ for (int i = 0; i < cues.Count; i++)
+ {
+ positions[i] = cues[i].Position;
+ }
+ return positions;
+ }
+ }
+
+ ///
+ /// Gets labels for the embedded cues
+ ///
+ /// Array containing the labels
+ public string[] CueLabels
+ {
+ get
+ {
+ string[] labels = new string[cues.Count];
+ for (int i = 0; i < cues.Count; i++)
+ {
+ labels[i] = cues[i].Label;
+ }
+ return labels;
+ }
+ }
+
+ ///
+ /// Creates a cue list from the cue RIFF chunk and the list RIFF chunk
+ ///
+ /// The data contained in the cue chunk
+ /// The data contained in the list chunk
+ internal CueList(byte[] cueChunkData, byte[] listChunkData)
+ {
+ int cueCount = BitConverter.ToInt32(cueChunkData, 0);
+ Dictionary cueIndex = new Dictionary();
+ int[] positions = new int[cueCount];
+ int cue = 0;
+
+ for (int p = 4; cueChunkData.Length - p >= 24; p += 24, cue++)
+ {
+ cueIndex[BitConverter.ToInt32(cueChunkData, p)] = cue;
+ positions[cue] = BitConverter.ToInt32(cueChunkData, p + 20);
+ }
+
+ string[] labels = new string[cueCount];
+ int labelLength = 0;
+ int cueID = 0;
+
+ Int32 labelChunkID = ChunkIdentifier.ChunkIdentifierToInt32("labl");
+ for (int p = 4; listChunkData.Length - p >= 16; p += labelLength + labelLength % 2 + 12)
+ {
+ if (BitConverter.ToInt32(listChunkData, p) == labelChunkID)
+ {
+ labelLength = BitConverter.ToInt32(listChunkData, p + 4) - 4;
+ cueID = BitConverter.ToInt32(listChunkData, p + 8);
+ cue = cueIndex[cueID];
+ labels[cue] = Encoding.Default.GetString(listChunkData, p + 12, labelLength - 1);
+ }
+ }
+
+ for (int i = 0; i < cueCount; i++)
+ {
+ cues.Add(new Cue(positions[i], labels[i]));
+ }
+ }
+
+ ///
+ /// Gets the cues as the concatenated cue and list RIFF chunks.
+ ///
+ /// RIFF chunks containing the cue data
+ internal byte[] GetRIFFChunks()
+ {
+ if (this.Count == 0)
+ {
+ return null;
+ }
+ else
+ {
+ int cueChunkLength = 12 + 24 * this.Count;
+ int listChunkLength = 12;
+ int labelChunkLength = 0;
+ for (int i = 0; i < this.Count; i++)
+ {
+ labelChunkLength = this[i].Label.Length + 1;
+ listChunkLength += labelChunkLength + labelChunkLength % 2 + 12;
+ }
+
+ byte[] chunks = new byte[cueChunkLength + listChunkLength];
+ Int32 cueChunkID = ChunkIdentifier.ChunkIdentifierToInt32("cue ");
+ Int32 dataChunkID = ChunkIdentifier.ChunkIdentifierToInt32("data");
+ Int32 listChunkID = ChunkIdentifier.ChunkIdentifierToInt32("LIST");
+ Int32 adtlTypeID = ChunkIdentifier.ChunkIdentifierToInt32("adtl");
+ Int32 labelChunkID = ChunkIdentifier.ChunkIdentifierToInt32("labl");
+
+ using (MemoryStream stream = new MemoryStream(chunks))
+ {
+ using (BinaryWriter writer = new BinaryWriter(stream))
+ {
+ writer.Write(cueChunkID);
+ writer.Write(cueChunkLength - 8);
+ writer.Write(this.Count);
+ for (int cue = 0; cue < this.Count; cue++)
+ {
+ int position = this[cue].Position;
+
+ writer.Write(cue);
+ writer.Write(position);
+ writer.Write(dataChunkID);
+ writer.Seek(8, SeekOrigin.Current);
+ writer.Write(position);
+ }
+ writer.Write(listChunkID);
+ writer.Write(listChunkLength - 8);
+ writer.Write(adtlTypeID);
+ for (int cue = 0; cue < this.Count; cue++)
+ {
+ writer.Write(labelChunkID);
+ writer.Write(this[cue].Label.Length + 1 + 4);
+ writer.Write(cue);
+ writer.Write(Encoding.Default.GetBytes(this[cue].Label.ToCharArray()));
+ if (this[cue].Label.Length % 2 == 0)
+ {
+ writer.Seek(2, SeekOrigin.Current);
+ }
+ else
+ {
+ writer.Seek(1, SeekOrigin.Current);
+ }
+ }
+ }
+ }
+ return chunks;
+ }
+ }
+
+ ///
+ /// Number of cues
+ ///
+ public int Count
+ {
+ get { return cues.Count; }
+ }
+
+ ///
+ /// Accesses the cue at the specified index
+ ///
+ ///
+ ///
+ public Cue this[int index]
+ {
+ get { return cues[index]; }
+ }
+
+ ///
+ /// Checks if the cue and list chunks exist and if so, creates a cue list
+ ///
+ internal static CueList FromChunks(WaveFileReader reader)
+ {
+ CueList cueList = null;
+ byte[] cueChunkData = null;
+ byte[] listChunkData = null;
+
+ foreach (RiffChunk chunk in reader.ExtraChunks)
+ {
+ if (chunk.IdentifierAsString.ToLower() == "cue ")
+ {
+ cueChunkData = reader.GetChunkData(chunk);
+ }
+ else if (chunk.IdentifierAsString.ToLower() == "list")
+ {
+ listChunkData = reader.GetChunkData(chunk);
+ }
+ }
+ if (cueChunkData != null && listChunkData != null)
+ {
+ cueList = new CueList(cueChunkData, listChunkData);
+ }
+ return cueList;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/CueWaveFileReader.cs b/NAudio/Wave/WaveStreams/CueWaveFileReader.cs
new file mode 100644
index 00000000..f45c8c5e
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/CueWaveFileReader.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// A wave file reader supporting cue reading
+ ///
+ public class CueWaveFileReader : WaveFileReader
+ {
+ private CueList cues = null;
+
+ ///
+ /// Loads a wavefile and supports reading cues
+ ///
+ ///
+ public CueWaveFileReader(string fileName)
+ : base(fileName)
+ {
+ }
+
+ ///
+ /// Cue List (can be null if cues not present)
+ ///
+ public CueList Cues
+ {
+ get
+ {
+ if (cues == null)
+ {
+ cues = CueList.FromChunks(this);
+ }
+ return cues;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/ISampleNotifier.cs b/NAudio/Wave/WaveStreams/ISampleNotifier.cs
new file mode 100644
index 00000000..a76c71d3
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/ISampleNotifier.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// An interface for WaveStreams which can report notification of individual samples
+ ///
+ public interface ISampleNotifier
+ {
+ ///
+ /// A sample has been detected
+ ///
+ event EventHandler Sample;
+ }
+
+ ///
+ /// Sample event arguments
+ ///
+ public class SampleEventArgs : EventArgs
+ {
+ ///
+ /// Left sample
+ ///
+ public float Left { get; set; }
+ ///
+ /// Right sample
+ ///
+ public float Right { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public SampleEventArgs(float left, float right)
+ {
+ this.Left = left;
+ this.Right = right;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/MediaFoundationReader.cs b/NAudio/Wave/WaveStreams/MediaFoundationReader.cs
new file mode 100644
index 00000000..bdbdf11e
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/MediaFoundationReader.cs
@@ -0,0 +1,325 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using NAudio.CoreAudioApi.Interfaces;
+using NAudio.MediaFoundation;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Class for reading any file that Media Foundation can play
+ /// Will only work in Windows Vista and above
+ /// Automatically converts to PCM
+ /// If it is a video file with multiple audio streams, it will pick out the first audio stream
+ ///
+ public class MediaFoundationReader : WaveStream
+ {
+ private WaveFormat waveFormat;
+ private readonly long length;
+ private readonly MediaFoundationReaderSettings settings;
+ private readonly string file;
+ private IMFSourceReader pReader;
+
+ private long position;
+
+ ///
+ /// Allows customisation of this reader class
+ ///
+ public class MediaFoundationReaderSettings
+ {
+ ///
+ /// Sets up the default settings for MediaFoundationReader
+ ///
+ public MediaFoundationReaderSettings()
+ {
+ RepositionInRead = true;
+ }
+
+ ///
+ /// Allows us to request IEEE float output (n.b. no guarantee this will be accepted)
+ ///
+ public bool RequestFloatOutput { get; set; }
+ ///
+ /// If true, the reader object created in the constructor is used in Read
+ /// Should only be set to true if you are working entirely on an STA thread, or
+ /// entirely with MTA threads.
+ ///
+ public bool SingleReaderObject { get; set; }
+ ///
+ /// If true, the reposition does not happen immediately, but waits until the
+ /// next call to read to be processed.
+ ///
+ public bool RepositionInRead { get; set; }
+ }
+
+
+ ///
+ /// Creates a new MediaFoundationReader based on the supplied file
+ ///
+ /// Filename (can also be a URL e.g. http:// mms:// file://)
+ public MediaFoundationReader(string file)
+ : this(file, new MediaFoundationReaderSettings())
+ {
+ }
+
+
+ ///
+ /// Creates a new MediaFoundationReader based on the supplied file
+ ///
+ /// Filename
+ /// Advanced settings
+ public MediaFoundationReader(string file, MediaFoundationReaderSettings settings)
+ {
+ MediaFoundationApi.Startup();
+ this.settings = settings;
+ this.file = file;
+ var reader = CreateReader(settings);
+
+ waveFormat = GetCurrentWaveFormat(reader);
+
+ reader.SetStreamSelection(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, true);
+ length = GetLength(reader);
+
+ if (settings.SingleReaderObject)
+ {
+ pReader = reader;
+ }
+ }
+
+ private WaveFormat GetCurrentWaveFormat(IMFSourceReader reader)
+ {
+ IMFMediaType uncompressedMediaType;
+ reader.GetCurrentMediaType(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, out uncompressedMediaType);
+
+ // Two ways to query it, first is to ask for properties (second is to convert into WaveFormatEx using MFCreateWaveFormatExFromMFMediaType)
+ var outputMediaType = new MediaType(uncompressedMediaType);
+ Guid actualMajorType = outputMediaType.MajorType;
+ Debug.Assert(actualMajorType == MediaTypes.MFMediaType_Audio);
+ Guid audioSubType = outputMediaType.SubType;
+ int channels = outputMediaType.ChannelCount;
+ int bits = outputMediaType.BitsPerSample;
+ int sampleRate = outputMediaType.SampleRate;
+
+ return audioSubType == AudioSubtypes.MFAudioFormat_PCM
+ ? new WaveFormat(sampleRate, bits, channels)
+ : WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
+ }
+
+ ///
+ /// Creates the reader (overridable by )
+ ///
+ protected virtual IMFSourceReader CreateReader(MediaFoundationReaderSettings settings)
+ {
+ IMFSourceReader reader;
+ MediaFoundationInterop.MFCreateSourceReaderFromURL(file, null, out reader);
+ reader.SetStreamSelection(MediaFoundationInterop.MF_SOURCE_READER_ALL_STREAMS, false);
+ reader.SetStreamSelection(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, true);
+
+ // Create a partial media type indicating that we want uncompressed PCM audio
+
+ var partialMediaType = new MediaType();
+ partialMediaType.MajorType = MediaTypes.MFMediaType_Audio;
+ partialMediaType.SubType = settings.RequestFloatOutput ? AudioSubtypes.MFAudioFormat_Float : AudioSubtypes.MFAudioFormat_PCM;
+
+ // set the media type
+ // can return MF_E_INVALIDMEDIATYPE if not supported
+ reader.SetCurrentMediaType(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, IntPtr.Zero, partialMediaType.MediaFoundationObject);
+ return reader;
+ }
+
+ private long GetLength(IMFSourceReader reader)
+ {
+ PropVariant variant;
+ // http://msdn.microsoft.com/en-gb/library/windows/desktop/dd389281%28v=vs.85%29.aspx#getting_file_duration
+ int hResult = reader.GetPresentationAttribute(MediaFoundationInterop.MF_SOURCE_READER_MEDIASOURCE,
+ MediaFoundationAttributes.MF_PD_DURATION, out variant);
+ if (hResult == MediaFoundationErrors.MF_E_ATTRIBUTENOTFOUND)
+ {
+ // this doesn't support telling us its duration (might be streaming)
+ return 0;
+ }
+ if (hResult != 0)
+ {
+ Marshal.ThrowExceptionForHR(hResult);
+ }
+ var lengthInBytes = (((long)variant.Value) * waveFormat.AverageBytesPerSecond) / 10000000L;
+ variant.Clear();
+ return lengthInBytes;
+ }
+
+ private byte[] decoderOutputBuffer;
+ private int decoderOutputOffset;
+ private int decoderOutputCount;
+
+ private void EnsureBuffer(int bytesRequired)
+ {
+ if (decoderOutputBuffer == null || decoderOutputBuffer.Length < bytesRequired)
+ {
+ decoderOutputBuffer = new byte[bytesRequired];
+ }
+ }
+
+ ///
+ /// Reads from this wave stream
+ ///
+ /// Buffer to read into
+ /// Offset in buffer
+ /// Bytes required
+ /// Number of bytes read; 0 indicates end of stream
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (pReader == null)
+ {
+ pReader = CreateReader(settings);
+ }
+ if (repositionTo != -1)
+ {
+ Reposition(repositionTo);
+ }
+
+ int bytesWritten = 0;
+ // read in any leftovers from last time
+ if (decoderOutputCount > 0)
+ {
+ bytesWritten += ReadFromDecoderBuffer(buffer, offset, count - bytesWritten);
+ }
+
+ while (bytesWritten < count)
+ {
+ IMFSample pSample;
+ MF_SOURCE_READER_FLAG dwFlags;
+ ulong timestamp;
+ int actualStreamIndex;
+ pReader.ReadSample(MediaFoundationInterop.MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, out actualStreamIndex, out dwFlags, out timestamp, out pSample);
+ if ((dwFlags & MF_SOURCE_READER_FLAG.MF_SOURCE_READERF_ENDOFSTREAM) != 0)
+ {
+ // reached the end of the stream
+ break;
+ }
+ else if ((dwFlags & MF_SOURCE_READER_FLAG.MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED) != 0)
+ {
+ waveFormat = GetCurrentWaveFormat(pReader);
+ OnWaveFormatChanged();
+ // carry on, but user must handle the change of format
+ }
+ else if (dwFlags != 0)
+ {
+ throw new InvalidOperationException(String.Format("MediaFoundationReadError {0}", dwFlags));
+ }
+
+ IMFMediaBuffer pBuffer;
+ pSample.ConvertToContiguousBuffer(out pBuffer);
+ IntPtr pAudioData;
+ int cbBuffer;
+ int pcbMaxLength;
+ pBuffer.Lock(out pAudioData, out pcbMaxLength, out cbBuffer);
+ EnsureBuffer(cbBuffer);
+ Marshal.Copy(pAudioData, decoderOutputBuffer, 0, cbBuffer);
+ decoderOutputOffset = 0;
+ decoderOutputCount = cbBuffer;
+
+ bytesWritten += ReadFromDecoderBuffer(buffer, offset + bytesWritten, count - bytesWritten);
+
+
+ pBuffer.Unlock();
+ Marshal.ReleaseComObject(pBuffer);
+ Marshal.ReleaseComObject(pSample);
+ }
+ position += bytesWritten;
+ return bytesWritten;
+ }
+
+ private int ReadFromDecoderBuffer(byte[] buffer, int offset, int needed)
+ {
+ int bytesFromDecoderOutput = Math.Min(needed, decoderOutputCount);
+ Array.Copy(decoderOutputBuffer, decoderOutputOffset, buffer, offset, bytesFromDecoderOutput);
+ decoderOutputOffset += bytesFromDecoderOutput;
+ decoderOutputCount -= bytesFromDecoderOutput;
+ if (decoderOutputCount == 0)
+ {
+ decoderOutputOffset = 0;
+ }
+ return bytesFromDecoderOutput;
+ }
+
+ ///
+ /// WaveFormat of this stream (n.b. this is after converting to PCM)
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ /// The bytesRequired of this stream in bytes (n.b may not be accurate)
+ ///
+ public override long Length
+ {
+ get
+ {
+ return length;
+ }
+ }
+
+ ///
+ /// Current position within this stream
+ ///
+ public override long Position
+ {
+ get { return position; }
+ set
+ {
+ if (value < 0)
+ throw new ArgumentOutOfRangeException("value", "Position cannot be less than 0");
+ if (settings.RepositionInRead)
+ {
+ repositionTo = value;
+ position = value; // for gui apps, make it look like we have alread processed the reposition
+ }
+ else
+ {
+ Reposition(value);
+ }
+ }
+ }
+
+ private long repositionTo = -1;
+
+ private void Reposition(long desiredPosition)
+ {
+ long nsPosition = (10000000L * repositionTo) / waveFormat.AverageBytesPerSecond;
+ var pv = PropVariant.FromLong(nsPosition);
+ // should pass in a variant of type VT_I8 which is a long containing time in 100nanosecond units
+ pReader.SetCurrentPosition(Guid.Empty, ref pv);
+ decoderOutputCount = 0;
+ decoderOutputOffset = 0;
+ position = desiredPosition;
+ repositionTo = -1;// clear the flag
+ }
+
+ ///
+ /// Cleans up after finishing with this reader
+ ///
+ /// true if called from Dispose
+ protected override void Dispose(bool disposing)
+ {
+ if (pReader != null)
+ {
+ Marshal.ReleaseComObject(pReader);
+ pReader = null;
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// WaveFormat has changed
+ ///
+ public event EventHandler WaveFormatChanged;
+
+ private void OnWaveFormatChanged()
+ {
+ var handler = WaveFormatChanged;
+ if (handler != null) handler(this, EventArgs.Empty);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/Mp3FileReader.cs b/NAudio/Wave/WaveStreams/Mp3FileReader.cs
new file mode 100644
index 00000000..9e349934
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/Mp3FileReader.cs
@@ -0,0 +1,440 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace NAudio.Wave
+{
+ class Mp3Index
+ {
+ public long FilePosition { get; set; }
+ public long SamplePosition { get; set; }
+ public int SampleCount { get; set; }
+ public int ByteCount { get; set; }
+ }
+
+ ///
+ /// Class for reading from MP3 files
+ ///
+ public class Mp3FileReader : WaveStream
+ {
+ private readonly WaveFormat waveFormat;
+ private Stream mp3Stream;
+ private readonly long mp3DataLength;
+ private readonly long dataStartPosition;
+
+ ///
+ /// The MP3 wave format (n.b. NOT the output format of this stream - see the WaveFormat property)
+ ///
+ public Mp3WaveFormat Mp3WaveFormat { get; private set; }
+
+ private readonly Id3v2Tag id3v2Tag;
+ private readonly XingHeader xingHeader;
+ private readonly byte[] id3v1Tag;
+ private readonly bool ownInputStream;
+
+ private List tableOfContents;
+ private int tocIndex;
+
+ private long totalSamples;
+ private readonly int bytesPerSample;
+
+ private IMp3FrameDecompressor decompressor;
+
+ private readonly byte[] decompressBuffer;
+ private int decompressBufferOffset;
+ private int decompressLeftovers;
+ private bool repositionedFlag;
+
+ private readonly object repositionLock = new object();
+
+ /// Supports opening a MP3 file
+ public Mp3FileReader(string mp3FileName)
+ : this(File.OpenRead(mp3FileName))
+ {
+ ownInputStream = true;
+ }
+
+ /// Supports opening a MP3 file
+ /// MP3 File name
+ /// Factory method to build a frame decompressor
+ public Mp3FileReader(string mp3FileName, FrameDecompressorBuilder frameDecompressorBuilder)
+ : this(File.OpenRead(mp3FileName), frameDecompressorBuilder)
+ {
+ ownInputStream = true;
+ }
+
+ ///
+ /// Opens MP3 from a stream rather than a file
+ /// Will not dispose of this stream itself
+ ///
+ /// The incoming stream containing MP3 data
+ public Mp3FileReader(Stream inputStream)
+ : this (inputStream, CreateAcmFrameDecompressor)
+ {
+
+ }
+
+ ///
+ /// Opens MP3 from a stream rather than a file
+ /// Will not dispose of this stream itself
+ ///
+ /// The incoming stream containing MP3 data
+ /// Factory method to build a frame decompressor
+ public Mp3FileReader(Stream inputStream, FrameDecompressorBuilder frameDecompressorBuilder)
+ {
+ // Calculated as a double to minimize rounding errors
+
+ mp3Stream = inputStream;
+ id3v2Tag = Id3v2Tag.ReadTag(mp3Stream);
+
+ dataStartPosition = mp3Stream.Position;
+ var firstFrame = Mp3Frame.LoadFromStream(mp3Stream);
+ double bitRate = firstFrame.BitRate;
+ xingHeader = XingHeader.LoadXingHeader(firstFrame);
+ // If the header exists, we can skip over it when decoding the rest of the file
+ if (xingHeader != null) dataStartPosition = mp3Stream.Position;
+
+ // workaround for a longstanding issue with some files failing to load
+ // because they report a spurious sample rate change
+ var secondFrame = Mp3Frame.LoadFromStream(mp3Stream);
+ if (secondFrame != null &&
+ (secondFrame.SampleRate != firstFrame.SampleRate ||
+ secondFrame.ChannelMode != firstFrame.ChannelMode))
+ {
+ // assume that the first frame was some kind of VBR/LAME header that we failed to recognise properly
+ dataStartPosition = secondFrame.FileOffset;
+ // forget about the first frame, the second one is the first one we really care about
+ firstFrame = secondFrame;
+ }
+
+ this.mp3DataLength = mp3Stream.Length - dataStartPosition;
+
+ // try for an ID3v1 tag as well
+ mp3Stream.Position = mp3Stream.Length - 128;
+ byte[] tag = new byte[128];
+ mp3Stream.Read(tag, 0, 128);
+ if (tag[0] == 'T' && tag[1] == 'A' && tag[2] == 'G')
+ {
+ id3v1Tag = tag;
+ this.mp3DataLength -= 128;
+ }
+
+ mp3Stream.Position = dataStartPosition;
+
+ // create a temporary MP3 format before we know the real bitrate
+ this.Mp3WaveFormat = new Mp3WaveFormat(firstFrame.SampleRate, firstFrame.ChannelMode == ChannelMode.Mono ? 1 : 2, firstFrame.FrameLength, (int)bitRate);
+
+ CreateTableOfContents();
+ this.tocIndex = 0;
+
+ // [Bit rate in Kilobits/sec] = [Length in kbits] / [time in seconds]
+ // = [Length in bits ] / [time in milliseconds]
+
+ // Note: in audio, 1 kilobit = 1000 bits.
+ bitRate = (mp3DataLength * 8.0 / TotalSeconds());
+
+ mp3Stream.Position = dataStartPosition;
+
+ // now we know the real bitrate we can create an accurate
+ this.Mp3WaveFormat = new Mp3WaveFormat(firstFrame.SampleRate, firstFrame.ChannelMode == ChannelMode.Mono ? 1 : 2, firstFrame.FrameLength, (int)bitRate);
+ decompressor = frameDecompressorBuilder(Mp3WaveFormat);
+ this.waveFormat = decompressor.OutputFormat;
+ this.bytesPerSample = (decompressor.OutputFormat.BitsPerSample) / 8 * decompressor.OutputFormat.Channels;
+ // no MP3 frames have more than 1152 samples in them
+ // some MP3s I seem to get double
+ this.decompressBuffer = new byte[1152 * bytesPerSample * 2];
+ }
+
+ ///
+ /// Function that can create an MP3 Frame decompressor
+ ///
+ /// A WaveFormat object describing the MP3 file format
+ /// An MP3 Frame decompressor
+ public delegate IMp3FrameDecompressor FrameDecompressorBuilder(WaveFormat mp3Format);
+
+ ///
+ /// Creates an ACM MP3 Frame decompressor. This is the default with NAudio
+ ///
+ /// A WaveFormat object based
+ ///
+ public static IMp3FrameDecompressor CreateAcmFrameDecompressor(WaveFormat mp3Format)
+ {
+ // new DmoMp3FrameDecompressor(this.Mp3WaveFormat);
+ return new AcmMp3FrameDecompressor(mp3Format);
+ }
+
+ private void CreateTableOfContents()
+ {
+ try
+ {
+ // Just a guess at how many entries we'll need so the internal array need not resize very much
+ // 400 bytes per frame is probably a good enough approximation.
+ tableOfContents = new List((int)(mp3DataLength / 400));
+ Mp3Frame frame = null;
+ do
+ {
+ var index = new Mp3Index();
+ index.FilePosition = mp3Stream.Position;
+ index.SamplePosition = totalSamples;
+ frame = ReadNextFrame(false);
+ if (frame != null)
+ {
+ ValidateFrameFormat(frame);
+
+ totalSamples += frame.SampleCount;
+ index.SampleCount = frame.SampleCount;
+ index.ByteCount = (int)(mp3Stream.Position - index.FilePosition);
+ tableOfContents.Add(index);
+ }
+ } while (frame != null);
+ }
+ catch (EndOfStreamException)
+ {
+ // not necessarily a problem
+ }
+ }
+
+ private void ValidateFrameFormat(Mp3Frame frame)
+ {
+ if (frame.SampleRate != Mp3WaveFormat.SampleRate)
+ {
+ string message =
+ String.Format(
+ "Got a frame at sample rate {0}, in an MP3 with sample rate {1}. Mp3FileReader does not support sample rate changes.",
+ frame.SampleRate, Mp3WaveFormat.SampleRate);
+ throw new InvalidOperationException(message);
+ }
+ int channels = frame.ChannelMode == ChannelMode.Mono ? 1 : 2;
+ if (channels != Mp3WaveFormat.Channels)
+ {
+ string message =
+ String.Format(
+ "Got a frame with channel mode {0}, in an MP3 with {1} channels. Mp3FileReader does not support changes to channel count.",
+ frame.ChannelMode, Mp3WaveFormat.Channels);
+ throw new InvalidOperationException(message);
+ }
+ }
+
+ ///
+ /// Gets the total length of this file in milliseconds.
+ ///
+ private double TotalSeconds()
+ {
+ return (double)this.totalSamples / Mp3WaveFormat.SampleRate;
+ }
+
+ ///
+ /// ID3v2 tag if present
+ ///
+ public Id3v2Tag Id3v2Tag
+ {
+ get { return id3v2Tag; }
+ }
+
+ ///
+ /// ID3v1 tag if present
+ ///
+ public byte[] Id3v1Tag
+ {
+ get { return id3v1Tag; }
+ }
+
+ ///
+ /// Reads the next mp3 frame
+ ///
+ /// Next mp3 frame, or null if EOF
+ public Mp3Frame ReadNextFrame()
+ {
+ return ReadNextFrame(true);
+ }
+
+ ///
+ /// Reads the next mp3 frame
+ ///
+ /// Next mp3 frame, or null if EOF
+ private Mp3Frame ReadNextFrame(bool readData)
+ {
+ Mp3Frame frame = null;
+ try
+ {
+ frame = Mp3Frame.LoadFromStream(mp3Stream, readData);
+ if (frame != null)
+ {
+ tocIndex++;
+ }
+ }
+ catch (EndOfStreamException)
+ {
+ // suppress for now - it means we unexpectedly got to the end of the stream
+ // half way through
+ }
+ return frame;
+ }
+
+ ///
+ /// This is the length in bytes of data available to be read out from the Read method
+ /// (i.e. the decompressed MP3 length)
+ /// n.b. this may return 0 for files whose length is unknown
+ ///
+ public override long Length
+ {
+ get
+ {
+ return this.totalSamples * this.bytesPerSample; // assume converting to 16 bit (n.b. may have to check if this includes) //length;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ ///
+ ///
+ ///
+ public override long Position
+ {
+ get
+ {
+ if (tocIndex >= tableOfContents.Count)
+ {
+ return this.Length;
+ }
+ else
+ {
+ return (tableOfContents[tocIndex].SamplePosition * this.bytesPerSample) + decompressBufferOffset;
+ }
+ }
+ set
+ {
+ lock (repositionLock)
+ {
+ value = Math.Max(Math.Min(value, Length), 0);
+ var samplePosition = value / this.bytesPerSample;
+ Mp3Index mp3Index = null;
+ for (int index = 0; index < tableOfContents.Count; index++)
+ {
+ if (tableOfContents[index].SamplePosition >= samplePosition)
+ {
+ mp3Index = tableOfContents[index];
+ tocIndex = index;
+ break;
+ }
+ }
+ if (mp3Index != null)
+ {
+ // perform the reposition
+ mp3Stream.Position = mp3Index.FilePosition;
+ }
+ else
+ {
+ // we are repositioning to the end of the data
+ mp3Stream.Position = mp3DataLength + dataStartPosition;
+ }
+ decompressBufferOffset = 0;
+ decompressLeftovers = 0;
+ repositionedFlag = true;
+ }
+ }
+ }
+
+ ///
+ /// Reads decompressed PCM data from our MP3 file.
+ ///
+ public override int Read(byte[] sampleBuffer, int offset, int numBytes)
+ {
+ int bytesRead = 0;
+ lock (repositionLock)
+ {
+ if (decompressLeftovers != 0)
+ {
+ int toCopy = Math.Min(decompressLeftovers, numBytes);
+ Array.Copy(decompressBuffer, decompressBufferOffset, sampleBuffer, offset, toCopy);
+ decompressLeftovers -= toCopy;
+ if (decompressLeftovers == 0)
+ {
+ decompressBufferOffset = 0;
+ }
+ else
+ {
+ decompressBufferOffset += toCopy;
+ }
+ bytesRead += toCopy;
+ offset += toCopy;
+ }
+
+ while (bytesRead < numBytes)
+ {
+ Mp3Frame frame = ReadNextFrame();
+ if (frame != null)
+ {
+ if (repositionedFlag)
+ {
+ decompressor.Reset();
+ repositionedFlag = false;
+ }
+ int decompressed = decompressor.DecompressFrame(frame, decompressBuffer, 0);
+
+ int toCopy = Math.Min(decompressed, numBytes - bytesRead);
+ Array.Copy(decompressBuffer, 0, sampleBuffer, offset, toCopy);
+ if (toCopy < decompressed)
+ {
+ decompressBufferOffset = toCopy;
+ decompressLeftovers = decompressed - toCopy;
+ }
+ else
+ {
+ // no lefovers
+ decompressBufferOffset = 0;
+ decompressLeftovers = 0;
+ }
+ offset += toCopy;
+ bytesRead += toCopy;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ Debug.Assert(bytesRead <= numBytes, "MP3 File Reader read too much");
+ return bytesRead;
+ }
+
+ ///
+ /// Xing header if present
+ ///
+ public XingHeader XingHeader
+ {
+ get { return xingHeader; }
+ }
+
+ ///
+ /// Disposes this WaveStream
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (mp3Stream != null)
+ {
+ if (ownInputStream)
+ {
+ mp3Stream.Dispose();
+ }
+ mp3Stream = null;
+ }
+ if (decompressor != null)
+ {
+ decompressor.Dispose();
+ decompressor = null;
+ }
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/RawSourceWaveStream.cs b/NAudio/Wave/WaveStreams/RawSourceWaveStream.cs
new file mode 100644
index 00000000..d99b8357
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/RawSourceWaveStream.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveStream that simply passes on data from its source stream
+ /// (e.g. a MemoryStream)
+ ///
+ public class RawSourceWaveStream : WaveStream
+ {
+ private Stream sourceStream;
+ private WaveFormat waveFormat;
+
+ ///
+ /// Initialises a new instance of RawSourceWaveStream
+ ///
+ /// The source stream containing raw audio
+ /// The waveformat of the audio in the source stream
+ public RawSourceWaveStream(Stream sourceStream, WaveFormat waveFormat)
+ {
+ this.sourceStream = sourceStream;
+ this.waveFormat = waveFormat;
+ }
+
+ ///
+ /// The WaveFormat of this stream
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return this.waveFormat; }
+ }
+
+ ///
+ /// The length in bytes of this stream (if supported)
+ ///
+ public override long Length
+ {
+ get { return this.sourceStream.Length; }
+ }
+
+ ///
+ /// The current position in this stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return this.sourceStream.Position;
+ }
+ set
+ {
+ this.sourceStream.Position = value;
+ }
+ }
+
+ ///
+ /// Reads data from the stream
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return sourceStream.Read(buffer, offset, count);
+ }
+ }
+}
+
diff --git a/NAudio/Wave/WaveStreams/ResamplerDmoStream.cs b/NAudio/Wave/WaveStreams/ResamplerDmoStream.cs
new file mode 100644
index 00000000..b4b89d19
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/ResamplerDmoStream.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Dmo;
+using System.Diagnostics;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Wave Stream for converting between sample rates
+ ///
+ public class ResamplerDmoStream : WaveStream
+ {
+ private readonly IWaveProvider inputProvider;
+ private readonly WaveStream inputStream;
+ private readonly WaveFormat outputFormat;
+ private DmoOutputDataBuffer outputBuffer;
+ private DmoResampler dmoResampler;
+ private MediaBuffer inputMediaBuffer;
+ private long position;
+
+ ///
+ /// WaveStream to resample using the DMO Resampler
+ ///
+ /// Input Stream
+ /// Desired Output Format
+ public ResamplerDmoStream(IWaveProvider inputProvider, WaveFormat outputFormat)
+ {
+ this.inputProvider = inputProvider;
+ this.inputStream = inputProvider as WaveStream;
+ this.outputFormat = outputFormat;
+ this.dmoResampler = new DmoResampler();
+ if (!dmoResampler.MediaObject.SupportsInputWaveFormat(0, inputProvider.WaveFormat))
+ {
+ throw new ArgumentException("Unsupported Input Stream format", "inputStream");
+ }
+
+ dmoResampler.MediaObject.SetInputWaveFormat(0, inputProvider.WaveFormat);
+ if (!dmoResampler.MediaObject.SupportsOutputWaveFormat(0, outputFormat))
+ {
+ throw new ArgumentException("Unsupported Output Stream format", "outputStream");
+ }
+
+ dmoResampler.MediaObject.SetOutputWaveFormat(0, outputFormat);
+ if (inputStream != null)
+ {
+ position = InputToOutputPosition(inputStream.Position);
+ }
+ this.inputMediaBuffer = new MediaBuffer(inputProvider.WaveFormat.AverageBytesPerSecond);
+ this.outputBuffer = new DmoOutputDataBuffer(outputFormat.AverageBytesPerSecond);
+ }
+
+ ///
+ /// Stream Wave Format
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get { return outputFormat; }
+ }
+
+ private long InputToOutputPosition(long inputPosition)
+ {
+ double ratio = (double)outputFormat.AverageBytesPerSecond
+ / inputProvider.WaveFormat.AverageBytesPerSecond;
+ long outputPosition = (long)(inputPosition * ratio);
+ if (outputPosition % outputFormat.BlockAlign != 0)
+ {
+ outputPosition -= outputPosition % outputFormat.BlockAlign;
+ }
+ return outputPosition;
+ }
+
+ private long OutputToInputPosition(long outputPosition)
+ {
+ double ratio = (double)outputFormat.AverageBytesPerSecond
+ / inputProvider.WaveFormat.AverageBytesPerSecond;
+ long inputPosition = (long)(outputPosition / ratio);
+ if (inputPosition % inputProvider.WaveFormat.BlockAlign != 0)
+ {
+ inputPosition -= inputPosition % inputProvider.WaveFormat.BlockAlign;
+ }
+ return inputPosition;
+ }
+
+ ///
+ /// Stream length in bytes
+ ///
+ public override long Length
+ {
+ get
+ {
+ if (this.inputStream == null)
+ {
+ throw new InvalidOperationException("Cannot report length if the input was an IWaveProvider");
+ }
+ return InputToOutputPosition(inputStream.Length);
+ }
+ }
+
+ ///
+ /// Stream position in bytes
+ ///
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ if (this.inputStream == null)
+ {
+ throw new InvalidOperationException("Cannot set position if the input was not a WaveStream");
+ }
+ inputStream.Position = OutputToInputPosition(value);
+ position = InputToOutputPosition(inputStream.Position);
+ dmoResampler.MediaObject.Discontinuity(0);
+ }
+ }
+
+ ///
+ /// Reads data from input stream
+ ///
+ /// buffer
+ /// offset into buffer
+ /// Bytes required
+ /// Number of bytes read
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int outputBytesProvided = 0;
+
+ while (outputBytesProvided < count)
+ {
+ if (dmoResampler.MediaObject.IsAcceptingData(0))
+ {
+ // 1. Read from the input stream
+ int inputBytesRequired = (int)OutputToInputPosition(count - outputBytesProvided);
+ byte[] inputByteArray = new byte[inputBytesRequired];
+ int inputBytesRead = inputProvider.Read(inputByteArray, 0, inputBytesRequired);
+ if (inputBytesRead == 0)
+ {
+ //Debug.WriteLine("ResamplerDmoStream.Read: No input data available");
+ break;
+ }
+ // 2. copy into our DMO's input buffer
+ inputMediaBuffer.LoadData(inputByteArray, inputBytesRead);
+
+ // 3. Give the input buffer to the DMO to process
+ dmoResampler.MediaObject.ProcessInput(0, inputMediaBuffer, DmoInputDataBufferFlags.None, 0, 0);
+
+ outputBuffer.MediaBuffer.SetLength(0);
+ outputBuffer.StatusFlags = DmoOutputDataBufferFlags.None;
+
+ // 4. Now ask the DMO for some output data
+ dmoResampler.MediaObject.ProcessOutput(DmoProcessOutputFlags.None, 1, new DmoOutputDataBuffer[] { outputBuffer });
+
+ if (outputBuffer.Length == 0)
+ {
+ Debug.WriteLine("ResamplerDmoStream.Read: No output data available");
+ break;
+ }
+
+ // 5. Now get the data out of the output buffer
+ outputBuffer.RetrieveData(buffer, offset + outputBytesProvided);
+ outputBytesProvided += outputBuffer.Length;
+
+ Debug.Assert(!outputBuffer.MoreDataAvailable, "have not implemented more data available yet");
+ }
+ else
+ {
+ Debug.Assert(false, "have not implemented not accepting logic yet");
+ }
+ }
+
+ position += outputBytesProvided;
+ return outputBytesProvided;
+ }
+
+ ///
+ /// Dispose
+ ///
+ /// True if disposing (not from finalizer)
+ protected override void Dispose(bool disposing)
+ {
+ if (inputMediaBuffer != null)
+ {
+ inputMediaBuffer.Dispose();
+ inputMediaBuffer = null;
+ }
+ outputBuffer.Dispose();
+ if (dmoResampler != null)
+ {
+ //resampler.Dispose(); s
+ dmoResampler = null;
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/RiffChunk.cs b/NAudio/Wave/WaveStreams/RiffChunk.cs
new file mode 100644
index 00000000..44fba5b6
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/RiffChunk.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Holds information about a RIFF file chunk
+ ///
+ public class RiffChunk
+ {
+ ///
+ /// Creates a RiffChunk object
+ ///
+ public RiffChunk(int identifier, int length, long streamPosition)
+ {
+ Identifier = identifier;
+ Length = length;
+ StreamPosition = streamPosition;
+ }
+
+ ///
+ /// The chunk identifier
+ ///
+ public int Identifier { get; private set; }
+
+ ///
+ /// The chunk identifier converted to a string
+ ///
+ public string IdentifierAsString
+ {
+ get
+ {
+ return Encoding.UTF8.GetString(BitConverter.GetBytes(Identifier));
+ }
+ }
+
+ ///
+ /// The chunk length
+ ///
+ public int Length { get; private set; }
+
+ ///
+ /// The stream position this chunk is located at
+ ///
+ public long StreamPosition { get; private set; }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/SimpleCompressorStream.cs b/NAudio/Wave/WaveStreams/SimpleCompressorStream.cs
new file mode 100644
index 00000000..687f7b23
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/SimpleCompressorStream.cs
@@ -0,0 +1,317 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Dsp;
+
+namespace NAudio.Wave
+{
+ ///
+ /// A simple compressor
+ ///
+ public class SimpleCompressorStream : WaveStream
+ {
+ private WaveStream sourceStream;
+ private readonly SimpleCompressor simpleCompressor;
+ private byte[] sourceBuffer; // buffer used by Read function
+ private bool enabled;
+ private readonly int channels;
+ private readonly int bytesPerSample;
+ private readonly object lockObject = new object();
+
+ ///
+ /// Create a new simple compressor stream
+ ///
+ /// Source stream
+ public SimpleCompressorStream(WaveStream sourceStream)
+ {
+ this.sourceStream = sourceStream;
+ this.channels = sourceStream.WaveFormat.Channels;
+ this.bytesPerSample = sourceStream.WaveFormat.BitsPerSample / 8;
+ simpleCompressor = new SimpleCompressor(5.0, 10.0, sourceStream.WaveFormat.SampleRate);
+ simpleCompressor.Threshold = 16;
+ simpleCompressor.Ratio = 6;
+ simpleCompressor.MakeUpGain = 16;
+
+ }
+
+ ///
+ /// Make-up Gain
+ ///
+ public double MakeUpGain
+ {
+ get
+ {
+ return simpleCompressor.MakeUpGain;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ simpleCompressor.MakeUpGain = value;
+ }
+ }
+ }
+
+ ///
+ /// Threshold
+ ///
+ public double Threshold
+ {
+ get
+ {
+ return simpleCompressor.Threshold;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ simpleCompressor.Threshold = value;
+ }
+ }
+ }
+
+ ///
+ /// Ratio
+ ///
+ public double Ratio
+ {
+ get
+ {
+ return simpleCompressor.Ratio;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ simpleCompressor.Ratio = value;
+ }
+ }
+ }
+
+ ///
+ /// Attack time
+ ///
+ public double Attack
+ {
+ get
+ {
+ return simpleCompressor.Attack;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ simpleCompressor.Attack = value;
+ }
+ }
+ }
+
+ ///
+ /// Release time
+ ///
+ public double Release
+ {
+ get
+ {
+ return simpleCompressor.Release;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ simpleCompressor.Release = value;
+ }
+ }
+ }
+
+
+ ///
+ /// Determine whether the stream has the required amount of data.
+ ///
+ /// Number of bytes of data required from the stream.
+ /// Flag indicating whether the required amount of data is avialable.
+ public override bool HasData(int count)
+ {
+ return sourceStream.HasData(count);
+ }
+
+
+ ///
+ /// Turns gain on or off
+ ///
+ public bool Enabled
+ {
+ get
+ {
+ return enabled;
+ }
+ set
+ {
+ enabled = value;
+ }
+ }
+
+
+ ///
+ /// Returns the stream length
+ ///
+ public override long Length
+ {
+ get
+ {
+ return sourceStream.Length;
+ }
+ }
+
+ ///
+ /// Gets or sets the current position in the stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return sourceStream.Position;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ sourceStream.Position = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets the WaveFormat of this stream
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get
+ {
+ return sourceStream.WaveFormat;
+ }
+ }
+
+ private void ReadSamples(byte[] buffer, int start, out double left, out double right)
+ {
+ if (bytesPerSample == 4)
+ {
+ left = BitConverter.ToSingle(buffer, start);
+ if (channels > 1)
+ {
+ right = BitConverter.ToSingle(buffer, start + bytesPerSample);
+ }
+ else
+ {
+ right = left;
+ }
+ }
+ else if (bytesPerSample == 2)
+ {
+ left = BitConverter.ToInt16(buffer, start) / 32768.0;
+ if (channels > 1)
+ {
+ right = BitConverter.ToInt16(buffer, start + bytesPerSample) / 32768.0;
+ }
+ else
+ {
+ right = left;
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException(String.Format("Unsupported bytes per sample: {0}", bytesPerSample));
+ }
+ }
+
+ private void WriteSamples(byte[] buffer, int start, double left, double right)
+ {
+ if (bytesPerSample == 4)
+ {
+ Array.Copy(BitConverter.GetBytes((float)left), 0, buffer, start, bytesPerSample);
+ if (channels > 1)
+ {
+ Array.Copy(BitConverter.GetBytes((float)right), 0, buffer, start + bytesPerSample, bytesPerSample);
+ }
+ }
+ else if (bytesPerSample == 2)
+ {
+ Array.Copy(BitConverter.GetBytes((short)(left * 32768.0)), 0, buffer, start, bytesPerSample);
+ if (channels > 1)
+ {
+ Array.Copy(BitConverter.GetBytes((short)(right * 32768.0)), 0, buffer, start + bytesPerSample, bytesPerSample);
+ }
+ }
+ }
+
+ ///
+ /// Reads bytes from this stream
+ ///
+ /// Buffer to read into
+ /// Offset in array to read into
+ /// Number of bytes to read
+ /// Number of bytes read
+ public override int Read(byte[] array, int offset, int count)
+ {
+ lock (lockObject)
+ {
+ if (Enabled)
+ {
+ if (sourceBuffer == null || sourceBuffer.Length < count)
+ sourceBuffer = new byte[count];
+ int sourceBytesRead = sourceStream.Read(sourceBuffer, 0, count);
+ int sampleCount = sourceBytesRead / (bytesPerSample * channels);
+ for (int sample = 0; sample < sampleCount; sample++)
+ {
+ int start = sample * bytesPerSample * channels;
+ double in1;
+ double in2;
+ ReadSamples(sourceBuffer, start, out in1, out in2);
+ simpleCompressor.Process(ref in1, ref in2);
+ WriteSamples(array, offset + start, in1, in2);
+ }
+ return count;
+ }
+ else
+ {
+ return sourceStream.Read(array, offset, count);
+ }
+ }
+
+ }
+
+ ///
+ /// Disposes this stream
+ ///
+ /// true if the user called this
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Release managed resources.
+ if (sourceStream != null)
+ {
+ sourceStream.Dispose();
+ sourceStream = null;
+ }
+ }
+ // Release unmanaged resources.
+ // Set large fields to null.
+ // Call Dispose on your base class.
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// Gets the block alignment for this stream
+ ///
+ public override int BlockAlign
+ {
+ get
+ {
+ // TODO: investigate forcing 20ms
+ return sourceStream.BlockAlign;
+ }
+ }
+ }
+}
+
diff --git a/NAudio/Wave/WaveStreams/Wave32To16Stream.cs b/NAudio/Wave/WaveStreams/Wave32To16Stream.cs
new file mode 100644
index 00000000..bace9f03
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/Wave32To16Stream.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveStream that converts 32 bit audio back down to 16 bit, clipping if necessary
+ ///
+ public class Wave32To16Stream : WaveStream
+ {
+ private WaveStream sourceStream;
+ private readonly WaveFormat waveFormat;
+ private readonly long length;
+ private long position;
+ private bool clip;
+ private float volume;
+ private readonly object lockObject = new object();
+
+ ///
+ /// Creates a new Wave32To16Stream
+ ///
+ /// the source stream
+ public Wave32To16Stream(WaveStream sourceStream)
+ {
+ if (sourceStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ throw new ArgumentException("Only 32 bit Floating point supported");
+ if (sourceStream.WaveFormat.BitsPerSample != 32)
+ throw new ArgumentException("Only 32 bit Floating point supported");
+
+ waveFormat = new WaveFormat(sourceStream.WaveFormat.SampleRate, 16, sourceStream.WaveFormat.Channels);
+ this.volume = 1.0f;
+ this.sourceStream = sourceStream;
+ length = sourceStream.Length / 2;
+ position = sourceStream.Position / 2;
+ }
+
+ ///
+ /// Sets the volume for this stream. 1.0f is full scale
+ ///
+ public float Volume
+ {
+ get
+ {
+ return volume;
+ }
+ set
+ {
+ volume = value;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override int BlockAlign
+ {
+ get
+ {
+ return sourceStream.BlockAlign / 2;
+ }
+ }
+
+
+ ///
+ /// Returns the stream length
+ ///
+ public override long Length
+ {
+ get
+ {
+ return length;
+ }
+ }
+
+ ///
+ /// Gets or sets the current position in the stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ // make sure we don't get out of sync
+ value -= (value % BlockAlign);
+ sourceStream.Position = value * 2;
+ position = value;
+ }
+ }
+ }
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// Destination buffer
+ /// Offset into destination buffer
+ ///
+ /// Number of bytes read.
+ public override int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ lock (lockObject)
+ {
+ byte[] sourceBuffer = new byte[numBytes*2];
+ int bytesRead = sourceStream.Read(sourceBuffer, 0, numBytes*2);
+ Convert32To16(destBuffer, offset, sourceBuffer, bytesRead);
+ position += (bytesRead/2);
+ return bytesRead/2;
+ }
+ }
+
+ ///
+ /// Conversion to 16 bit and clipping
+ ///
+ private unsafe void Convert32To16(byte[] destBuffer, int offset, byte[] sourceBuffer, int bytesRead)
+ {
+ fixed (byte* pDestBuffer = &destBuffer[offset],
+ pSourceBuffer = &sourceBuffer[0])
+ {
+ short* psDestBuffer = (short*)pDestBuffer;
+ float* pfSourceBuffer = (float*)pSourceBuffer;
+
+ int samplesRead = bytesRead / 4;
+ for (int n = 0; n < samplesRead; n++)
+ {
+ float sampleVal = pfSourceBuffer[n] * volume;
+ if (sampleVal > 1.0f)
+ {
+ psDestBuffer[n] = short.MaxValue;
+ clip = true;
+ }
+ else if (sampleVal < -1.0f)
+ {
+ psDestBuffer[n] = short.MinValue;
+ clip = true;
+ }
+ else
+ {
+ psDestBuffer[n] = (short)(sampleVal * 32767);
+ }
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get
+ {
+ return waveFormat;
+ }
+ }
+
+ ///
+ /// Clip indicator. Can be reset.
+ ///
+ public bool Clip
+ {
+ get
+ {
+ return clip;
+ }
+ set
+ {
+ clip = value;
+ }
+ }
+
+ ///
+ /// Disposes this WaveStream
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (sourceStream != null)
+ {
+ sourceStream.Dispose();
+ sourceStream = null;
+ }
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveChannel32.cs b/NAudio/Wave/WaveStreams/WaveChannel32.cs
new file mode 100644
index 00000000..2b0d1ac4
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveChannel32.cs
@@ -0,0 +1,288 @@
+using System;
+using System.Collections.Generic;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Represents Channel for the WaveMixerStream
+ /// 32 bit output and 16 bit input
+ /// It's output is always stereo
+ /// The input stream can be panned
+ ///
+ public class WaveChannel32 : WaveStream, ISampleNotifier
+ {
+ private WaveStream sourceStream;
+ private readonly WaveFormat waveFormat;
+ private readonly long length;
+ private readonly int destBytesPerSample;
+ private readonly int sourceBytesPerSample;
+ private volatile float volume;
+ private volatile float pan;
+ private long position;
+ private readonly ISampleChunkConverter sampleProvider;
+ private readonly object lockObject = new object();
+
+ ///
+ /// Creates a new WaveChannel32
+ ///
+ /// the source stream
+ /// stream volume (1 is 0dB)
+ /// pan control (-1 to 1)
+ public WaveChannel32(WaveStream sourceStream, float volume, float pan)
+ {
+ PadWithZeroes = true;
+
+ var providers = new ISampleChunkConverter[]
+ {
+ new Mono8SampleChunkConverter(),
+ new Stereo8SampleChunkConverter(),
+ new Mono16SampleChunkConverter(),
+ new Stereo16SampleChunkConverter(),
+ new Mono24SampleChunkConverter(),
+ new Stereo24SampleChunkConverter(),
+ new MonoFloatSampleChunkConverter(),
+ new StereoFloatSampleChunkConverter(),
+ };
+ foreach (var provider in providers)
+ {
+ if (provider.Supports(sourceStream.WaveFormat))
+ {
+ this.sampleProvider = provider;
+ break;
+ }
+ }
+
+ if (this.sampleProvider == null)
+ {
+ throw new ArgumentException("Unsupported sourceStream format");
+ }
+
+ // always outputs stereo 32 bit
+ waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sourceStream.WaveFormat.SampleRate, 2);
+ destBytesPerSample = 8; // includes stereo factoring
+
+ this.sourceStream = sourceStream;
+ this.volume = volume;
+ this.pan = pan;
+ sourceBytesPerSample = sourceStream.WaveFormat.Channels * sourceStream.WaveFormat.BitsPerSample / 8;
+
+ length = SourceToDest(sourceStream.Length);
+ position = 0;
+ }
+
+ private long SourceToDest(long sourceBytes)
+ {
+ return (sourceBytes / sourceBytesPerSample) * destBytesPerSample;
+ }
+
+ private long DestToSource(long destBytes)
+ {
+ return (destBytes / destBytesPerSample) * sourceBytesPerSample;
+ }
+
+ ///
+ /// Creates a WaveChannel32 with default settings
+ ///
+ /// The source stream
+ public WaveChannel32(WaveStream sourceStream)
+ :
+ this(sourceStream, 1.0f, 0.0f)
+ {
+ }
+
+ ///
+ /// Gets the block alignment for this WaveStream
+ ///
+ public override int BlockAlign
+ {
+ get
+ {
+ return (int)SourceToDest(sourceStream.BlockAlign);
+ }
+ }
+
+ ///
+ /// Returns the stream length
+ ///
+ public override long Length
+ {
+ get
+ {
+ return length;
+ }
+ }
+
+ ///
+ /// Gets or sets the current position in the stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ // make sure we don't get out of sync
+ value -= (value % BlockAlign);
+ if (value < 0)
+ {
+ sourceStream.Position = 0;
+ }
+ else
+ {
+ sourceStream.Position = DestToSource(value);
+ }
+ // source stream may not have accepted the reposition we gave it
+ position = SourceToDest(sourceStream.Position);
+ }
+ }
+ }
+
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// The destination buffer
+ /// Offset into the destination buffer
+ /// Number of bytes read
+ /// Number of bytes read.
+ public override int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ lock (lockObject)
+ {
+ int bytesWritten = 0;
+ WaveBuffer destWaveBuffer = new WaveBuffer(destBuffer);
+
+ // 1. fill with silence
+ if (position < 0)
+ {
+ bytesWritten = (int) Math.Min(numBytes, 0 - position);
+ for (int n = 0; n < bytesWritten; n++)
+ destBuffer[n + offset] = 0;
+ }
+ if (bytesWritten < numBytes)
+ {
+ this.sampleProvider.LoadNextChunk(sourceStream, (numBytes - bytesWritten)/8);
+ float left, right;
+
+ int outIndex = (offset/4) + bytesWritten/4;
+ while (this.sampleProvider.GetNextSample(out left, out right) && bytesWritten < numBytes)
+ {
+ // implement better panning laws.
+ left = (pan <= 0) ? left : (left*(1 - pan)/2.0f);
+ right = (pan >= 0) ? right : (right*(pan + 1)/2.0f);
+ left *= volume;
+ right *= volume;
+ destWaveBuffer.FloatBuffer[outIndex++] = left;
+ destWaveBuffer.FloatBuffer[outIndex++] = right;
+ bytesWritten += 8;
+ if (Sample != null) RaiseSample(left, right);
+ }
+ }
+ // 3. Fill out with zeroes
+ if (PadWithZeroes && bytesWritten < numBytes)
+ {
+ Array.Clear(destBuffer, offset + bytesWritten, numBytes - bytesWritten);
+ bytesWritten = numBytes;
+ }
+ position += bytesWritten;
+ return bytesWritten;
+ }
+ }
+
+ ///
+ /// If true, Read always returns the number of bytes requested
+ ///
+ public bool PadWithZeroes { get; set; }
+
+
+ ///
+ ///
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get
+ {
+ return waveFormat;
+ }
+ }
+
+ ///
+ /// Volume of this channel. 1.0 = full scale
+ ///
+ public float Volume
+ {
+ get { return volume; }
+ set { volume = value; }
+ }
+
+ ///
+ /// Pan of this channel (from -1 to 1)
+ ///
+ public float Pan
+ {
+ get { return pan; }
+ set { pan = value; }
+ }
+
+ ///
+ /// Determines whether this channel has any data to play
+ /// to allow optimisation to not read, but bump position forward
+ ///
+ public override bool HasData(int count)
+ {
+ // Check whether the source stream has data.
+ bool sourceHasData = sourceStream.HasData(count);
+
+ if (sourceHasData)
+ {
+ if (position + count < 0)
+ return false;
+ return (position < length) && (volume != 0);
+ }
+ return false;
+ }
+
+ ///
+ /// Disposes this WaveStream
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (sourceStream != null)
+ {
+ sourceStream.Dispose();
+ sourceStream = null;
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveChannel32 was not Disposed");
+ }
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// Sample
+ ///
+ public event EventHandler Sample;
+
+ // reuse the same object every time to avoid making lots of work for the garbage collector
+ private SampleEventArgs sampleEventArgs = new SampleEventArgs(0,0);
+
+ ///
+ /// Raise the sample event (no check for null because it has already been done)
+ ///
+ private void RaiseSample(float left, float right)
+ {
+ sampleEventArgs.Left = left;
+ sampleEventArgs.Right = right;
+ Sample(this, sampleEventArgs);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveFileReader.cs b/NAudio/Wave/WaveStreams/WaveFileReader.cs
new file mode 100644
index 00000000..e379e8b2
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveFileReader.cs
@@ -0,0 +1,260 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Collections.Generic;
+using System.Diagnostics;
+using NAudio.FileFormats.Wav;
+
+namespace NAudio.Wave
+{
+ /// This class supports the reading of WAV files,
+ /// providing a repositionable WaveStream that returns the raw data
+ /// contained in the WAV file
+ ///
+ public class WaveFileReader : WaveStream
+ {
+ private readonly WaveFormat waveFormat;
+ private readonly bool ownInput;
+ private readonly long dataPosition;
+ private readonly long dataChunkLength;
+ private readonly List chunks = new List();
+ private readonly object lockObject = new object();
+ private Stream waveStream;
+
+ /// Supports opening a WAV file
+ /// The WAV file format is a real mess, but we will only
+ /// support the basic WAV file format which actually covers the vast
+ /// majority of WAV files out there. For more WAV file format information
+ /// visit www.wotsit.org. If you have a WAV file that can't be read by
+ /// this class, email it to the NAudio project and we will probably
+ /// fix this reader to support it
+ ///
+ public WaveFileReader(String waveFile) :
+ this(File.OpenRead(waveFile))
+ {
+ ownInput = true;
+ }
+
+ ///
+ /// Creates a Wave File Reader based on an input stream
+ ///
+ /// The input stream containing a WAV file including header
+ public WaveFileReader(Stream inputStream)
+ {
+ this.waveStream = inputStream;
+ var chunkReader = new WaveFileChunkReader();
+ chunkReader.ReadWaveHeader(inputStream);
+ this.waveFormat = chunkReader.WaveFormat;
+ this.dataPosition = chunkReader.DataChunkPosition;
+ this.dataChunkLength = chunkReader.DataChunkLength;
+ this.chunks = chunkReader.RiffChunks;
+ Position = 0;
+ }
+
+ ///
+ /// Gets a list of the additional chunks found in this file
+ ///
+ public List ExtraChunks
+ {
+ get
+ {
+ return chunks;
+ }
+ }
+
+ ///
+ /// Gets the data for the specified chunk
+ ///
+ public byte[] GetChunkData(RiffChunk chunk)
+ {
+ long oldPosition = waveStream.Position;
+ waveStream.Position = chunk.StreamPosition;
+ byte[] data = new byte[chunk.Length];
+ waveStream.Read(data, 0, data.Length);
+ waveStream.Position = oldPosition;
+ return data;
+ }
+
+ ///
+ /// Cleans up the resources associated with this WaveFileReader
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Release managed resources.
+ if (waveStream != null)
+ {
+ // only dispose our source if we created it
+ if (ownInput)
+ {
+ waveStream.Close();
+ }
+ waveStream = null;
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveFileReader was not disposed");
+ }
+ // Release unmanaged resources.
+ // Set large fields to null.
+ // Call Dispose on your base class.
+ base.Dispose(disposing);
+ }
+
+ ///
+ ///
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get
+ {
+ return waveFormat;
+ }
+ }
+
+ ///
+ /// This is the length of audio data contained in this WAV file, in bytes
+ /// (i.e. the byte length of the data chunk, not the length of the WAV file itself)
+ ///
+ ///
+ public override long Length
+ {
+ get
+ {
+ return dataChunkLength;
+ }
+ }
+
+ ///
+ /// Number of Samples (if possible to calculate)
+ /// This currently does not take into account number of channels, so
+ /// divide again by number of channels if you want the number of
+ /// audio 'frames'
+ ///
+ public long SampleCount
+ {
+ get
+ {
+ if (waveFormat.Encoding == WaveFormatEncoding.Pcm ||
+ waveFormat.Encoding == WaveFormatEncoding.Extensible ||
+ waveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ return dataChunkLength / BlockAlign;
+ }
+ // n.b. if there is a fact chunk, you can use that to get the number of samples
+ throw new InvalidOperationException("Sample count is calculated only for the standard encodings");
+ }
+ }
+
+ ///
+ /// Position in the WAV data chunk.
+ ///
+ ///
+ public override long Position
+ {
+ get
+ {
+ return waveStream.Position - dataPosition;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ value = Math.Min(value, Length);
+ // make sure we don't get out of sync
+ value -= (value % waveFormat.BlockAlign);
+ waveStream.Position = value + dataPosition;
+ }
+ }
+ }
+
+ ///
+ /// Reads bytes from the Wave File
+ ///
+ ///
+ public override int Read(byte[] array, int offset, int count)
+ {
+ if (count % waveFormat.BlockAlign != 0)
+ {
+ throw new ArgumentException(String.Format("Must read complete blocks: requested {0}, block align is {1}",count,this.WaveFormat.BlockAlign));
+ }
+ lock (lockObject)
+ {
+ // sometimes there is more junk at the end of the file past the data chunk
+ if (Position + count > dataChunkLength)
+ {
+ count = (int) (dataChunkLength - Position);
+ }
+ return waveStream.Read(array, offset, count);
+ }
+ }
+
+ ///
+ /// Attempts to read the next sample or group of samples as floating point normalised into the range -1.0f to 1.0f
+ ///
+ /// An array of samples, 1 for mono, 2 for stereo etc. Null indicates end of file reached
+ ///
+ public float[] ReadNextSampleFrame()
+ {
+ switch (waveFormat.Encoding)
+ {
+ case WaveFormatEncoding.Pcm:
+ case WaveFormatEncoding.IeeeFloat:
+ case WaveFormatEncoding.Extensible: // n.b. not necessarily PCM, should probably write more code to handle this case
+ break;
+ default:
+ throw new InvalidOperationException("Only 16, 24 or 32 bit PCM or IEEE float audio data supported");
+ }
+ var sampleFrame = new float[waveFormat.Channels];
+ int bytesToRead = waveFormat.Channels*(waveFormat.BitsPerSample/8);
+ byte[] raw = new byte[bytesToRead];
+ int bytesRead = Read(raw, 0, bytesToRead);
+ if (bytesRead == 0) return null; // end of file
+ if (bytesRead < bytesToRead) throw new InvalidDataException("Unexpected end of file");
+ int offset = 0;
+ for (int channel = 0; channel < waveFormat.Channels; channel++)
+ {
+ if (waveFormat.BitsPerSample == 16)
+ {
+ sampleFrame[channel] = BitConverter.ToInt16(raw, offset)/32768f;
+ offset += 2;
+ }
+ else if (waveFormat.BitsPerSample == 24)
+ {
+ sampleFrame[channel] = (((sbyte)raw[offset + 2] << 16) | (raw[offset + 1] << 8) | raw[offset]) / 8388608f;
+ offset += 3;
+ }
+ else if (waveFormat.BitsPerSample == 32 && waveFormat.Encoding == WaveFormatEncoding.IeeeFloat)
+ {
+ sampleFrame[channel] = BitConverter.ToSingle(raw, offset);
+ offset += 4;
+ }
+ else if (waveFormat.BitsPerSample == 32)
+ {
+ sampleFrame[channel] = BitConverter.ToInt32(raw, offset) / (Int32.MaxValue + 1f);
+ offset += 4;
+ }
+ else
+ {
+ throw new InvalidOperationException("Unsupported bit depth");
+ }
+ }
+ return sampleFrame;
+ }
+
+ ///
+ /// Attempts to read a sample into a float. n.b. only applicable for uncompressed formats
+ /// Will normalise the value read into the range -1.0f to 1.0f if it comes from a PCM encoding
+ ///
+ /// False if the end of the WAV data chunk was reached
+ [Obsolete("Use ReadNextSampleFrame instead (this version does not support stereo properly)")]
+ public bool TryReadFloat(out float sampleValue)
+ {
+ var sf = ReadNextSampleFrame();
+ sampleValue = sf != null ? sf[0] : 0;
+ return sf != null;
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveFormatConversionStream.cs b/NAudio/Wave/WaveStreams/WaveFormatConversionStream.cs
new file mode 100644
index 00000000..3d2191e6
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveFormatConversionStream.cs
@@ -0,0 +1,276 @@
+using System;
+using System.Diagnostics;
+using NAudio.Wave.Compression;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveStream that passes through an ACM Codec
+ ///
+ public class WaveFormatConversionStream : WaveStream
+ {
+ private AcmStream conversionStream;
+ private WaveStream sourceStream;
+ private WaveFormat targetFormat;
+ private long length;
+ private long position;
+ private int preferredSourceReadSize;
+
+ ///
+ /// Creates a stream that can convert to PCM
+ ///
+ /// The source stream
+ /// A PCM stream
+ public static WaveStream CreatePcmStream(WaveStream sourceStream)
+ {
+ if (sourceStream.WaveFormat.Encoding == WaveFormatEncoding.Pcm)
+ {
+ return sourceStream;
+ }
+ WaveFormat pcmFormat = AcmStream.SuggestPcmFormat(sourceStream.WaveFormat);
+ if (pcmFormat.SampleRate < 8000)
+ {
+ if (sourceStream.WaveFormat.Encoding == WaveFormatEncoding.G723)
+ {
+ pcmFormat = new WaveFormat(8000, 16, 1);
+ }
+ else
+ {
+ throw new InvalidOperationException("Invalid suggested output format, please explicitly provide a target format");
+ }
+ }
+ return new WaveFormatConversionStream(pcmFormat, sourceStream);
+ }
+
+ ///
+ /// Create a new WaveFormat conversion stream
+ ///
+ /// Desired output format
+ /// Source stream
+ public WaveFormatConversionStream(WaveFormat targetFormat, WaveStream sourceStream)
+ {
+ this.sourceStream = sourceStream;
+ this.targetFormat = targetFormat;
+
+ conversionStream = new AcmStream(sourceStream.WaveFormat, targetFormat);
+ /*try
+ {
+ // work out how many bytes the entire input stream will convert to
+ length = conversionStream.SourceToDest((int)sourceStream.Length);
+ }
+ catch
+ {
+ Dispose();
+ throw;
+ }*/
+ length = EstimateSourceToDest((int)sourceStream.Length);
+
+ position = 0;
+ preferredSourceReadSize = Math.Min(sourceStream.WaveFormat.AverageBytesPerSecond, conversionStream.SourceBuffer.Length);
+ preferredSourceReadSize -= (preferredSourceReadSize%sourceStream.WaveFormat.BlockAlign);
+ }
+
+ ///
+ /// Converts source bytes to destination bytes
+ ///
+ [Obsolete("can be unreliable, use of this method not encouraged")]
+ public int SourceToDest(int source)
+ {
+ return (int) EstimateSourceToDest(source);
+ //return conversionStream.SourceToDest(source);
+ }
+
+ private long EstimateSourceToDest(long source)
+ {
+ var dest = ((source * targetFormat.AverageBytesPerSecond) / sourceStream.WaveFormat.AverageBytesPerSecond);
+ dest -= (dest % targetFormat.BlockAlign);
+ return dest;
+ }
+
+ private long EstimateDestToSource(long dest)
+ {
+ var source = ((dest * sourceStream.WaveFormat.AverageBytesPerSecond) / targetFormat.AverageBytesPerSecond);
+ source -= (source % sourceStream.WaveFormat.BlockAlign);
+ return (int)source;
+ }
+ ///
+ /// Converts destination bytes to source bytes
+ ///
+ [Obsolete("can be unreliable, use of this method not encouraged")]
+ public int DestToSource(int dest)
+ {
+ return (int)EstimateDestToSource(dest);
+ //return conversionStream.DestToSource(dest);
+ }
+
+ ///
+ /// Returns the stream length
+ ///
+ public override long Length
+ {
+ get
+ {
+ return length;
+ }
+ }
+
+ ///
+ /// Gets or sets the current position in the stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ // make sure we don't get out of sync
+ value -= (value % BlockAlign);
+
+ // this relies on conversionStream DestToSource and SourceToDest being reliable
+ var desiredSourcePosition = EstimateDestToSource(value); //conversionStream.DestToSource((int) value);
+ sourceStream.Position = desiredSourcePosition;
+ position = EstimateSourceToDest(sourceStream.Position); //conversionStream.SourceToDest((int)sourceStream.Position);
+ leftoverDestBytes = 0;
+ leftoverDestOffset = 0;
+ conversionStream.Reposition();
+ }
+ }
+
+ ///
+ /// Gets the WaveFormat of this stream
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get
+ {
+ return targetFormat;
+ }
+ }
+
+ private int leftoverDestBytes = 0;
+ private int leftoverDestOffset = 0;
+ private int leftoverSourceBytes = 0;
+ //private int leftoverSourceOffset = 0;
+
+ ///
+ /// Reads bytes from this stream
+ ///
+ /// Buffer to read into
+ /// Offset in buffer to read into
+ /// Number of bytes to read
+ /// Number of bytes read
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int bytesRead = 0;
+ if (count % BlockAlign != 0)
+ {
+ //throw new ArgumentException("Must read complete blocks");
+ count -= (count % BlockAlign);
+ }
+
+ while (bytesRead < count)
+ {
+ // first copy in any leftover destination bytes
+ int readFromLeftoverDest = Math.Min(count - bytesRead, leftoverDestBytes);
+ if (readFromLeftoverDest > 0)
+ {
+ Array.Copy(conversionStream.DestBuffer, leftoverDestOffset, buffer, offset+bytesRead, readFromLeftoverDest);
+ leftoverDestOffset += readFromLeftoverDest;
+ leftoverDestBytes -= readFromLeftoverDest;
+ bytesRead += readFromLeftoverDest;
+ }
+ if (bytesRead >= count)
+ {
+ // we've fulfilled the request from the leftovers alone
+ break;
+ }
+
+ // now we'll convert one full source buffer
+ if (leftoverSourceBytes > 0)
+ {
+ // TODO: still to be implemented: see moving the source position back below:
+ }
+
+ // always read our preferred size, we can always keep leftovers for the next call to Read if we get
+ // too much
+ int sourceBytesRead = sourceStream.Read(conversionStream.SourceBuffer, 0, preferredSourceReadSize);
+ if (sourceBytesRead == 0)
+ {
+ // we've reached the end of the input
+ break;
+ }
+
+ int sourceBytesConverted;
+ int destBytesConverted = conversionStream.Convert(sourceBytesRead, out sourceBytesConverted);
+ if (sourceBytesConverted == 0)
+ {
+ Debug.WriteLine(String.Format("Warning: couldn't convert anything from {0}", sourceBytesRead));
+ // no point backing up in this case as we're not going to manage to finish playing this
+ break;
+ }
+ else if (sourceBytesConverted < sourceBytesRead)
+ {
+ // cheat by backing up in the source stream (better to save the lefto
+ sourceStream.Position -= (sourceBytesRead - sourceBytesConverted);
+ }
+
+ if (destBytesConverted > 0)
+ {
+ int bytesRequired = count - bytesRead;
+ int toCopy = Math.Min(destBytesConverted, bytesRequired);
+
+ // save leftovers
+ if (toCopy < destBytesConverted)
+ {
+ leftoverDestBytes = destBytesConverted - toCopy;
+ leftoverDestOffset = toCopy;
+ }
+ Array.Copy(conversionStream.DestBuffer, 0, buffer, bytesRead + offset, toCopy);
+ bytesRead += toCopy;
+ }
+ else
+ {
+ // possible error here
+ Debug.WriteLine(string.Format("sourceBytesRead: {0}, sourceBytesConverted {1}, destBytesConverted {2}",
+ sourceBytesRead, sourceBytesConverted, destBytesConverted));
+ //Debug.Assert(false, "conversion stream returned nothing at all");
+ break;
+ }
+ }
+ position += bytesRead;
+ return bytesRead;
+ }
+
+ ///
+ /// Disposes this stream
+ ///
+ /// true if the user called this
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // Release managed resources.
+ if (conversionStream != null)
+ {
+ conversionStream.Dispose();
+ conversionStream = null;
+ }
+ if (sourceStream != null)
+ {
+ sourceStream.Dispose();
+ sourceStream = null;
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveFormatConversionStream was not disposed");
+ }
+ // Release unmanaged resources.
+ // Set large fields to null.
+ // Call Dispose on your base class.
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveInBuffer.cs b/NAudio/Wave/WaveStreams/WaveInBuffer.cs
new file mode 100644
index 00000000..c68bebbd
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveInBuffer.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// A buffer of Wave samples
+ ///
+ class WaveInBuffer : IDisposable
+ {
+ private readonly WaveHeader header;
+ private readonly Int32 bufferSize; // allocated bytes, may not be the same as bytes read
+ private readonly byte[] buffer;
+ private GCHandle hBuffer;
+ private IntPtr waveInHandle;
+ private GCHandle hHeader; // we need to pin the header structure
+ private GCHandle hThis; // for the user callback
+
+ ///
+ /// creates a new wavebuffer
+ ///
+ /// WaveIn device to write to
+ /// Buffer size in bytes
+ public WaveInBuffer(IntPtr waveInHandle, Int32 bufferSize)
+ {
+ this.bufferSize = bufferSize;
+ this.buffer = new byte[bufferSize];
+ this.hBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+ this.waveInHandle = waveInHandle;
+
+ header = new WaveHeader();
+ hHeader = GCHandle.Alloc(header, GCHandleType.Pinned);
+ header.dataBuffer = hBuffer.AddrOfPinnedObject();
+ header.bufferLength = bufferSize;
+ header.loops = 1;
+ hThis = GCHandle.Alloc(this);
+ header.userData = (IntPtr)hThis;
+
+ MmException.Try(WaveInterop.waveInPrepareHeader(waveInHandle, header, Marshal.SizeOf(header)), "waveInPrepareHeader");
+ //MmException.Try(WaveInterop.waveInAddBuffer(waveInHandle, header, Marshal.SizeOf(header)), "waveInAddBuffer");
+ }
+
+ ///
+ /// Place this buffer back to record more audio
+ ///
+ public void Reuse()
+ {
+ // TEST: we might not actually need to bother unpreparing and repreparing
+ MmException.Try(WaveInterop.waveInUnprepareHeader(waveInHandle, header, Marshal.SizeOf(header)), "waveUnprepareHeader");
+ MmException.Try(WaveInterop.waveInPrepareHeader(waveInHandle, header, Marshal.SizeOf(header)), "waveInPrepareHeader");
+ //System.Diagnostics.Debug.Assert(header.bytesRecorded == 0, "bytes recorded was not reset properly");
+ MmException.Try(WaveInterop.waveInAddBuffer(waveInHandle, header, Marshal.SizeOf(header)), "waveInAddBuffer");
+ }
+
+ #region Dispose Pattern
+
+ ///
+ /// Finalizer for this wave buffer
+ ///
+ ~WaveInBuffer()
+ {
+ Dispose(false);
+ System.Diagnostics.Debug.Assert(true, "WaveInBuffer was not disposed");
+ }
+
+ ///
+ /// Releases resources held by this WaveBuffer
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ Dispose(true);
+ }
+
+ ///
+ /// Releases resources held by this WaveBuffer
+ ///
+ protected void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // free managed resources
+ }
+ // free unmanaged resources
+ if (waveInHandle != IntPtr.Zero)
+ {
+ WaveInterop.waveInUnprepareHeader(waveInHandle, header, Marshal.SizeOf(header));
+ waveInHandle = IntPtr.Zero;
+ }
+ if (hHeader.IsAllocated)
+ hHeader.Free();
+ if (hBuffer.IsAllocated)
+ hBuffer.Free();
+ if (hThis.IsAllocated)
+ hThis.Free();
+
+ }
+
+ #endregion
+
+ ///
+ /// Provides access to the actual record buffer (for reading only)
+ ///
+ public byte[] Data
+ {
+ get
+ {
+ return buffer;
+ }
+ }
+
+ ///
+ /// Indicates whether the Done flag is set on this buffer
+ ///
+ public bool Done
+ {
+ get
+ {
+ return (header.flags & WaveHeaderFlags.Done) == WaveHeaderFlags.Done;
+ }
+ }
+
+
+ ///
+ /// Indicates whether the InQueue flag is set on this buffer
+ ///
+ public bool InQueue
+ {
+ get
+ {
+ return (header.flags & WaveHeaderFlags.InQueue) == WaveHeaderFlags.InQueue;
+ }
+ }
+
+ ///
+ /// Number of bytes recorded
+ ///
+ public int BytesRecorded
+ {
+ get
+ {
+ return header.bytesRecorded;
+ }
+ }
+
+ ///
+ /// The buffer size in bytes
+ ///
+ public Int32 BufferSize
+ {
+ get
+ {
+ return bufferSize;
+ }
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveMixerStream32.cs b/NAudio/Wave/WaveStreams/WaveMixerStream32.cs
new file mode 100644
index 00000000..553e90d3
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveMixerStream32.cs
@@ -0,0 +1,269 @@
+using System;
+using System.Collections.Generic;
+
+namespace NAudio.Wave
+{
+ ///
+ /// WaveStream that can mix together multiple 32 bit input streams
+ /// (Normally used with stereo input channels)
+ /// All channels must have the same number of inputs
+ ///
+ public class WaveMixerStream32 : WaveStream
+ {
+ private readonly List inputStreams;
+ private readonly object inputsLock;
+ private WaveFormat waveFormat;
+ private long length;
+ private long position;
+ private readonly int bytesPerSample;
+
+ ///
+ /// Creates a new 32 bit WaveMixerStream
+ ///
+ public WaveMixerStream32()
+ {
+ AutoStop = true;
+ waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
+ bytesPerSample = 4;
+ inputStreams = new List();
+ inputsLock = new object();
+ }
+
+ ///
+ /// Creates a new 32 bit WaveMixerStream
+ ///
+ /// An Array of WaveStreams - must all have the same format.
+ /// Use WaveChannel is designed for this purpose.
+ /// Automatically stop when all inputs have been read
+ /// Thrown if the input streams are not 32 bit floating point,
+ /// or if they have different formats to each other
+ public WaveMixerStream32(IEnumerable inputStreams, bool autoStop)
+ : this()
+ {
+ AutoStop = autoStop;
+
+ foreach (var inputStream in inputStreams)
+ {
+ AddInputStream(inputStream);
+ }
+ }
+
+ ///
+ /// Add a new input to the mixer
+ ///
+ /// The wave input to add
+ public void AddInputStream(WaveStream waveStream)
+ {
+ if (waveStream.WaveFormat.Encoding != WaveFormatEncoding.IeeeFloat)
+ throw new ArgumentException("Must be IEEE floating point", "waveStream");
+ if (waveStream.WaveFormat.BitsPerSample != 32)
+ throw new ArgumentException("Only 32 bit audio currently supported", "waveStream");
+
+ if (inputStreams.Count == 0)
+ {
+ // first one - set the format
+ int sampleRate = waveStream.WaveFormat.SampleRate;
+ int channels = waveStream.WaveFormat.Channels;
+ this.waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
+ }
+ else
+ {
+ if (!waveStream.WaveFormat.Equals(waveFormat))
+ throw new ArgumentException("All incoming channels must have the same format", "waveStream");
+ }
+
+ lock (inputsLock)
+ {
+ this.inputStreams.Add(waveStream);
+ this.length = Math.Max(this.length, waveStream.Length);
+ // get to the right point in this input file
+ waveStream.Position = Position;
+ }
+ }
+
+ ///
+ /// Remove a WaveStream from the mixer
+ ///
+ /// waveStream to remove
+ public void RemoveInputStream(WaveStream waveStream)
+ {
+ lock (inputsLock)
+ {
+ if (inputStreams.Remove(waveStream))
+ {
+ // recalculate the length
+ long newLength = 0;
+ foreach (var inputStream in inputStreams)
+ {
+ newLength = Math.Max(newLength, inputStream.Length);
+ }
+ length = newLength;
+ }
+ }
+ }
+
+ ///
+ /// The number of inputs to this mixer
+ ///
+ public int InputCount
+ {
+ get { return inputStreams.Count; }
+ }
+
+ ///
+ /// Automatically stop when all inputs have been read
+ ///
+ public bool AutoStop { get; set; }
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// buffer to read into
+ /// offset into buffer
+ /// number of bytes required
+ /// Number of bytes read.
+ /// Thrown if an invalid number of bytes requested
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (AutoStop)
+ {
+ if (position + count > length)
+ count = (int)(length - position);
+
+ // was a bug here, should be fixed now
+ System.Diagnostics.Debug.Assert(count >= 0, "length and position mismatch");
+ }
+
+
+ if (count % bytesPerSample != 0)
+ throw new ArgumentException("Must read an whole number of samples", "count");
+
+ // blank the buffer
+ Array.Clear(buffer, offset, count);
+ int bytesRead = 0;
+
+ // sum the channels in
+ var readBuffer = new byte[count];
+ lock (inputsLock)
+ {
+ foreach (var inputStream in inputStreams)
+ {
+ if (inputStream.HasData(count))
+ {
+ int readFromThisStream = inputStream.Read(readBuffer, 0, count);
+ // don't worry if input stream returns less than we requested - may indicate we have got to the end
+ bytesRead = Math.Max(bytesRead, readFromThisStream);
+ if (readFromThisStream > 0)
+ Sum32BitAudio(buffer, offset, readBuffer, readFromThisStream);
+ }
+ else
+ {
+ bytesRead = Math.Max(bytesRead, count);
+ inputStream.Position += count;
+ }
+ }
+ }
+ position += count;
+ return count;
+ }
+
+ ///
+ /// Actually performs the mixing
+ ///
+ static unsafe void Sum32BitAudio(byte[] destBuffer, int offset, byte[] sourceBuffer, int bytesRead)
+ {
+ fixed (byte* pDestBuffer = &destBuffer[offset],
+ pSourceBuffer = &sourceBuffer[0])
+ {
+ float* pfDestBuffer = (float*)pDestBuffer;
+ float* pfReadBuffer = (float*)pSourceBuffer;
+ int samplesRead = bytesRead / 4;
+ for (int n = 0; n < samplesRead; n++)
+ {
+ pfDestBuffer[n] += pfReadBuffer[n];
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override int BlockAlign
+ {
+ get
+ {
+ return waveFormat.BlockAlign; // inputStreams[0].BlockAlign;
+ }
+ }
+
+ ///
+ /// Length of this Wave Stream (in bytes)
+ ///
+ ///
+ public override long Length
+ {
+ get
+ {
+ return length;
+ }
+ }
+
+ ///
+ /// Position within this Wave Stream (in bytes)
+ ///
+ ///
+ public override long Position
+ {
+ get
+ {
+ // all streams are at the same position
+ return position;
+ }
+ set
+ {
+ lock (inputsLock)
+ {
+ value = Math.Min(value, Length);
+ foreach (WaveStream inputStream in inputStreams)
+ {
+ inputStream.Position = Math.Min(value, inputStream.Length);
+ }
+ this.position = value;
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get
+ {
+ return waveFormat;
+ }
+ }
+
+ ///
+ /// Disposes this WaveStream
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ lock (inputsLock)
+ {
+ foreach (WaveStream inputStream in inputStreams)
+ {
+ inputStream.Dispose();
+ }
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveMixerStream32 was not disposed");
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveOffsetStream.cs b/NAudio/Wave/WaveStreams/WaveOffsetStream.cs
new file mode 100644
index 00000000..6c43fff3
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveOffsetStream.cs
@@ -0,0 +1,251 @@
+using System;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Simply shifts the input stream in time, optionally
+ /// clipping its start and end.
+ /// (n.b. may include looping in the future)
+ ///
+ public class WaveOffsetStream : WaveStream
+ {
+ private WaveStream sourceStream;
+ private long audioStartPosition;
+ private long sourceOffsetBytes;
+ private long sourceLengthBytes;
+ private long length;
+ private readonly int bytesPerSample; // includes all channels
+ private long position;
+ private TimeSpan startTime;
+ private TimeSpan sourceOffset;
+ private TimeSpan sourceLength;
+ private readonly object lockObject = new object();
+
+ ///
+ /// Creates a new WaveOffsetStream
+ ///
+ /// the source stream
+ /// the time at which we should start reading from the source stream
+ /// amount to trim off the front of the source stream
+ /// length of time to play from source stream
+ public WaveOffsetStream(WaveStream sourceStream, TimeSpan startTime, TimeSpan sourceOffset, TimeSpan sourceLength)
+ {
+ if (sourceStream.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
+ throw new ArgumentException("Only PCM supported");
+ // TODO: add support for IEEE float + perhaps some others -
+ // anything with a fixed bytes per sample
+
+ this.sourceStream = sourceStream;
+ position = 0;
+ bytesPerSample = (sourceStream.WaveFormat.BitsPerSample / 8) * sourceStream.WaveFormat.Channels;
+ this.StartTime = startTime;
+ this.SourceOffset = sourceOffset;
+ this.SourceLength = sourceLength;
+
+ }
+
+ ///
+ /// Creates a WaveOffsetStream with default settings (no offset or pre-delay,
+ /// and whole length of source stream)
+ ///
+ /// The source stream
+ public WaveOffsetStream(WaveStream sourceStream)
+ : this(sourceStream, TimeSpan.Zero, TimeSpan.Zero, sourceStream.TotalTime)
+ {
+ }
+
+ ///
+ /// The length of time before which no audio will be played
+ ///
+ public TimeSpan StartTime
+ {
+ get
+ {
+ return startTime;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ startTime = value;
+ audioStartPosition = (long)(startTime.TotalSeconds * sourceStream.WaveFormat.SampleRate) * bytesPerSample;
+ // fix up our length and position
+ this.length = audioStartPosition + sourceLengthBytes;
+ Position = Position;
+ }
+ }
+ }
+
+ ///
+ /// An offset into the source stream from which to start playing
+ ///
+ public TimeSpan SourceOffset
+ {
+ get
+ {
+ return sourceOffset;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ sourceOffset = value;
+ sourceOffsetBytes = (long)(sourceOffset.TotalSeconds * sourceStream.WaveFormat.SampleRate) * bytesPerSample;
+ // fix up our position
+ Position = Position;
+ }
+ }
+ }
+
+ ///
+ /// Length of time to read from the source stream
+ ///
+ public TimeSpan SourceLength
+ {
+ get
+ {
+ return sourceLength;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ sourceLength = value;
+ sourceLengthBytes = (long)(sourceLength.TotalSeconds * sourceStream.WaveFormat.SampleRate) * bytesPerSample;
+ // fix up our length and position
+ this.length = audioStartPosition + sourceLengthBytes;
+ Position = Position;
+ }
+ }
+
+ }
+
+ ///
+ /// Gets the block alignment for this WaveStream
+ ///
+ public override int BlockAlign
+ {
+ get
+ {
+ return sourceStream.BlockAlign;
+ }
+ }
+
+ ///
+ /// Returns the stream length
+ ///
+ public override long Length
+ {
+ get
+ {
+ return length;
+ }
+ }
+
+ ///
+ /// Gets or sets the current position in the stream
+ ///
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ lock (lockObject)
+ {
+ // make sure we don't get out of sync
+ value -= (value % BlockAlign);
+ if (value < audioStartPosition)
+ sourceStream.Position = sourceOffsetBytes;
+ else
+ sourceStream.Position = sourceOffsetBytes + (value - audioStartPosition);
+ position = value;
+ }
+ }
+ }
+
+ ///
+ /// Reads bytes from this wave stream
+ ///
+ /// The destination buffer
+ /// Offset into the destination buffer
+ /// Number of bytes read
+ /// Number of bytes read.
+ public override int Read(byte[] destBuffer, int offset, int numBytes)
+ {
+ lock (lockObject)
+ {
+ int bytesWritten = 0;
+ // 1. fill with silence
+ if (position < audioStartPosition)
+ {
+ bytesWritten = (int)Math.Min(numBytes, audioStartPosition - position);
+ for (int n = 0; n < bytesWritten; n++)
+ destBuffer[n + offset] = 0;
+ }
+ if (bytesWritten < numBytes)
+ {
+ // don't read too far into source stream
+ int sourceBytesRequired = (int)Math.Min(
+ numBytes - bytesWritten,
+ sourceLengthBytes + sourceOffsetBytes - sourceStream.Position);
+ int read = sourceStream.Read(destBuffer, bytesWritten + offset, sourceBytesRequired);
+ bytesWritten += read;
+ }
+ // 3. Fill out with zeroes
+ for (int n = bytesWritten; n < numBytes; n++)
+ destBuffer[offset + n] = 0;
+ position += numBytes;
+ return numBytes;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public override WaveFormat WaveFormat
+ {
+ get
+ {
+ return sourceStream.WaveFormat;
+ }
+ }
+
+ ///
+ /// Determines whether this channel has any data to play
+ /// to allow optimisation to not read, but bump position forward
+ ///
+ public override bool HasData(int count)
+ {
+ if (position + count < audioStartPosition)
+ return false;
+ if (position >= length)
+ return false;
+ // Check whether the source stream has data.
+ // source stream should be in the right poisition
+ return sourceStream.HasData(count);
+ }
+
+ ///
+ /// Disposes this WaveStream
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (sourceStream != null)
+ {
+ sourceStream.Dispose();
+ sourceStream = null;
+ }
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert(false, "WaveOffsetStream was not Disposed");
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveOutBuffer.cs b/NAudio/Wave/WaveStreams/WaveOutBuffer.cs
new file mode 100644
index 00000000..16f45b06
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveOutBuffer.cs
@@ -0,0 +1,161 @@
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace NAudio.Wave
+{
+ ///
+ /// A buffer of Wave samples for streaming to a Wave Output device
+ ///
+ class WaveOutBuffer : IDisposable
+ {
+ private readonly WaveHeader header;
+ private readonly Int32 bufferSize; // allocated bytes, may not be the same as bytes read
+ private readonly byte[] buffer;
+ private readonly IWaveProvider waveStream;
+ private readonly object waveOutLock;
+ private GCHandle hBuffer;
+ private IntPtr hWaveOut;
+ private GCHandle hHeader; // we need to pin the header structure
+ private GCHandle hThis; // for the user callback
+
+ ///
+ /// creates a new wavebuffer
+ ///
+ /// WaveOut device to write to
+ /// Buffer size in bytes
+ /// Stream to provide more data
+ /// Lock to protect WaveOut API's from being called on >1 thread
+ public WaveOutBuffer(IntPtr hWaveOut, Int32 bufferSize, IWaveProvider bufferFillStream, object waveOutLock)
+ {
+ this.bufferSize = bufferSize;
+ this.buffer = new byte[bufferSize];
+ this.hBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+ this.hWaveOut = hWaveOut;
+ this.waveStream = bufferFillStream;
+ this.waveOutLock = waveOutLock;
+
+ header = new WaveHeader();
+ hHeader = GCHandle.Alloc(header, GCHandleType.Pinned);
+ header.dataBuffer = hBuffer.AddrOfPinnedObject();
+ header.bufferLength = bufferSize;
+ header.loops = 1;
+ hThis = GCHandle.Alloc(this);
+ header.userData = (IntPtr)hThis;
+ lock (waveOutLock)
+ {
+ MmException.Try(WaveInterop.waveOutPrepareHeader(hWaveOut, header, Marshal.SizeOf(header)), "waveOutPrepareHeader");
+ }
+ }
+
+ #region Dispose Pattern
+
+ ///
+ /// Finalizer for this wave buffer
+ ///
+ ~WaveOutBuffer()
+ {
+ Dispose(false);
+ System.Diagnostics.Debug.Assert(true, "WaveBuffer was not disposed");
+ }
+
+ ///
+ /// Releases resources held by this WaveBuffer
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
+ Dispose(true);
+ }
+
+ ///
+ /// Releases resources held by this WaveBuffer
+ ///
+ protected void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // free managed resources
+ }
+ // free unmanaged resources
+ if (hHeader.IsAllocated)
+ hHeader.Free();
+ if (hBuffer.IsAllocated)
+ hBuffer.Free();
+ if (hThis.IsAllocated)
+ hThis.Free();
+ if (hWaveOut != IntPtr.Zero)
+ {
+ lock (waveOutLock)
+ {
+ WaveInterop.waveOutUnprepareHeader(hWaveOut, header, Marshal.SizeOf(header));
+ }
+ hWaveOut = IntPtr.Zero;
+ }
+ }
+
+ #endregion
+
+ /// this is called by the WAVE callback and should be used to refill the buffer
+ internal bool OnDone()
+ {
+ int bytes;
+ lock (waveStream)
+ {
+ bytes = waveStream.Read(buffer, 0, buffer.Length);
+ }
+ if (bytes == 0)
+ {
+ return false;
+ }
+ else
+ {
+ for (int n = bytes; n < buffer.Length; n++)
+ {
+ buffer[n] = 0;
+ }
+ }
+ WriteToWaveOut();
+ return true;
+ }
+
+ ///
+ /// Whether the header's in queue flag is set
+ ///
+ public bool InQueue
+ {
+ get
+ {
+ return (header.flags & WaveHeaderFlags.InQueue) == WaveHeaderFlags.InQueue;
+ }
+ }
+
+ ///
+ /// The buffer size in bytes
+ ///
+ public Int32 BufferSize
+ {
+ get
+ {
+ return bufferSize;
+ }
+ }
+
+ private void WriteToWaveOut()
+ {
+ MmResult result;
+
+ lock (waveOutLock)
+ {
+ result = WaveInterop.waveOutWrite(hWaveOut, header, Marshal.SizeOf(header));
+ }
+ if (result != MmResult.NoError)
+ {
+ throw new MmException(result, "waveOutWrite");
+ }
+
+ GC.KeepAlive(this);
+ }
+
+ }
+}
diff --git a/NAudio/Wave/WaveStreams/WaveStream.cs b/NAudio/Wave/WaveStreams/WaveStream.cs
new file mode 100644
index 00000000..a2e86690
--- /dev/null
+++ b/NAudio/Wave/WaveStreams/WaveStream.cs
@@ -0,0 +1,139 @@
+// created on 27/12/2002 at 20:20
+using System;
+using System.IO;
+
+namespace NAudio.Wave
+{
+ ///
+ /// Base class for all WaveStream classes. Derives from stream.
+ ///
+ public abstract class WaveStream : Stream, IWaveProvider
+ {
+ ///
+ /// Retrieves the WaveFormat for this stream
+ ///
+ public abstract WaveFormat WaveFormat { get; }
+
+ // base class includes long Position get; set
+ // base class includes long Length get
+ // base class includes Read
+ // base class includes Dispose
+
+ ///
+ /// We can read from this stream
+ ///
+ public override bool CanRead { get { return true; } }
+
+ ///
+ /// We can seek within this stream
+ ///
+ public override bool CanSeek { get { return true; } }
+
+ ///
+ /// We can't write to this stream
+ ///
+ public override bool CanWrite { get { return false; } }
+
+ ///
+ /// Flush does not need to do anything
+ /// See
+ ///
+ public override void Flush() { }
+
+ ///
+ /// An alternative way of repositioning.
+ /// See
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ if (origin == SeekOrigin.Begin)
+ Position = offset;
+ else if (origin == SeekOrigin.Current)
+ Position += offset;
+ else
+ Position = Length + offset;
+ return Position;
+ }
+
+ ///
+ /// Sets the length of the WaveStream. Not Supported.
+ ///
+ ///
+ public override void SetLength(long length)
+ {
+ throw new NotSupportedException("Can't set length of a WaveFormatString");
+ }
+
+ ///
+ /// Writes to the WaveStream. Not Supported.
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException("Can't write to a WaveFormatString");
+ }
+
+ ///
+ /// The block alignment for this wavestream. Do not modify the Position
+ /// to anything that is not a whole multiple of this value
+ ///
+ public virtual int BlockAlign
+ {
+ get
+ {
+ return WaveFormat.BlockAlign;
+ }
+ }
+
+ ///
+ /// Moves forward or backwards the specified number of seconds in the stream
+ ///
+ /// Number of seconds to move, can be negative
+ public void Skip(int seconds)
+ {
+ long newPosition = Position + WaveFormat.AverageBytesPerSecond*seconds;
+ if (newPosition > Length)
+ Position = Length;
+ else if (newPosition < 0)
+ Position = 0;
+ else
+ Position = newPosition;
+ }
+
+ ///
+ /// The current position in the stream in Time format
+ ///
+ public virtual TimeSpan CurrentTime
+ {
+ get
+ {
+ return TimeSpan.FromSeconds((double)Position / WaveFormat.AverageBytesPerSecond);
+ }
+ set
+ {
+ Position = (long) (value.TotalSeconds * WaveFormat.AverageBytesPerSecond);
+ }
+ }
+
+ ///
+ /// Total length in real-time of the stream (may be an estimate for compressed files)
+ ///
+ public virtual TimeSpan TotalTime
+ {
+ get
+ {
+
+ return TimeSpan.FromSeconds((double) Length / WaveFormat.AverageBytesPerSecond);
+ }
+ }
+
+ ///
+ /// Whether the WaveStream has non-zero sample data at the current position for the
+ /// specified count
+ ///
+ /// Number of bytes to read
+ public virtual bool HasData(int count)
+ {
+ return Position < Length;
+ }
+ }
+}
diff --git a/NAudioDemo/AcmDemo/AcmPanel.Designer.cs b/NAudioDemo/AcmDemo/AcmPanel.Designer.cs
new file mode 100644
index 00000000..959cd8cb
--- /dev/null
+++ b/NAudioDemo/AcmDemo/AcmPanel.Designer.cs
@@ -0,0 +1,153 @@
+namespace NAudioDemo
+{
+ partial class AcmPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.listBoxAcmDrivers = new System.Windows.Forms.ListBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.buttonEncode = new System.Windows.Forms.Button();
+ this.checkBoxAutoLaunchConvertedFile = new System.Windows.Forms.CheckBox();
+ this.buttonChooseFormat = new System.Windows.Forms.Button();
+ this.buttonDecode = new System.Windows.Forms.Button();
+ this.richTextBox1 = new System.Windows.Forms.RichTextBox();
+ this.buttonAdd = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // listBoxAcmDrivers
+ //
+ this.listBoxAcmDrivers.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.listBoxAcmDrivers.FormattingEnabled = true;
+ this.listBoxAcmDrivers.Location = new System.Drawing.Point(12, 28);
+ this.listBoxAcmDrivers.Name = "listBoxAcmDrivers";
+ this.listBoxAcmDrivers.Size = new System.Drawing.Size(522, 95);
+ this.listBoxAcmDrivers.TabIndex = 0;
+ this.listBoxAcmDrivers.SelectedIndexChanged += new System.EventHandler(this.listBoxAcmDrivers_SelectedIndexChanged);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(12, 9);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(282, 13);
+ this.label1.TabIndex = 1;
+ this.label1.Text = "The following ACM Codecs are installed on your computer:";
+ //
+ // buttonEncode
+ //
+ this.buttonEncode.Location = new System.Drawing.Point(216, 129);
+ this.buttonEncode.Name = "buttonEncode";
+ this.buttonEncode.Size = new System.Drawing.Size(74, 23);
+ this.buttonEncode.TabIndex = 2;
+ this.buttonEncode.Text = "Encode";
+ this.buttonEncode.UseVisualStyleBackColor = true;
+ this.buttonEncode.Click += new System.EventHandler(this.buttonEncode_Click);
+ //
+ // checkBoxAutoLaunchConvertedFile
+ //
+ this.checkBoxAutoLaunchConvertedFile.AutoSize = true;
+ this.checkBoxAutoLaunchConvertedFile.Location = new System.Drawing.Point(376, 135);
+ this.checkBoxAutoLaunchConvertedFile.Name = "checkBoxAutoLaunchConvertedFile";
+ this.checkBoxAutoLaunchConvertedFile.Size = new System.Drawing.Size(158, 17);
+ this.checkBoxAutoLaunchConvertedFile.TabIndex = 5;
+ this.checkBoxAutoLaunchConvertedFile.Text = "Auto-Launch Converted File";
+ this.checkBoxAutoLaunchConvertedFile.UseVisualStyleBackColor = true;
+ //
+ // buttonChooseFormat
+ //
+ this.buttonChooseFormat.Location = new System.Drawing.Point(12, 129);
+ this.buttonChooseFormat.Name = "buttonChooseFormat";
+ this.buttonChooseFormat.Size = new System.Drawing.Size(114, 23);
+ this.buttonChooseFormat.TabIndex = 4;
+ this.buttonChooseFormat.Text = "Choose Format...";
+ this.buttonChooseFormat.UseVisualStyleBackColor = true;
+ this.buttonChooseFormat.Click += new System.EventHandler(this.buttonChooseFormat_Click);
+ //
+ // buttonDecode
+ //
+ this.buttonDecode.Location = new System.Drawing.Point(296, 129);
+ this.buttonDecode.Name = "buttonDecode";
+ this.buttonDecode.Size = new System.Drawing.Size(74, 23);
+ this.buttonDecode.TabIndex = 2;
+ this.buttonDecode.Text = "Decode";
+ this.buttonDecode.UseVisualStyleBackColor = true;
+ this.buttonDecode.Click += new System.EventHandler(this.buttonDecode_Click);
+ //
+ // richTextBox1
+ //
+ this.richTextBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.richTextBox1.Location = new System.Drawing.Point(12, 158);
+ this.richTextBox1.Name = "richTextBox1";
+ this.richTextBox1.Size = new System.Drawing.Size(522, 152);
+ this.richTextBox1.TabIndex = 6;
+ this.richTextBox1.Text = "";
+ //
+ // buttonAdd
+ //
+ this.buttonAdd.Location = new System.Drawing.Point(136, 129);
+ this.buttonAdd.Name = "buttonAdd";
+ this.buttonAdd.Size = new System.Drawing.Size(74, 23);
+ this.buttonAdd.TabIndex = 2;
+ this.buttonAdd.Text = "Add Codec";
+ this.buttonAdd.UseVisualStyleBackColor = true;
+ this.buttonAdd.Click += new System.EventHandler(this.buttonAdd_Click);
+ //
+ // AcmPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.richTextBox1);
+ this.Controls.Add(this.checkBoxAutoLaunchConvertedFile);
+ this.Controls.Add(this.buttonChooseFormat);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.listBoxAcmDrivers);
+ this.Controls.Add(this.buttonDecode);
+ this.Controls.Add(this.buttonAdd);
+ this.Controls.Add(this.buttonEncode);
+ this.Name = "AcmPanel";
+ this.Size = new System.Drawing.Size(546, 325);
+ this.Load += new System.EventHandler(this.AcmForm_Load);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ListBox listBoxAcmDrivers;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button buttonEncode;
+ private System.Windows.Forms.CheckBox checkBoxAutoLaunchConvertedFile;
+ private System.Windows.Forms.Button buttonChooseFormat;
+ private System.Windows.Forms.Button buttonDecode;
+ private System.Windows.Forms.RichTextBox richTextBox1;
+ private System.Windows.Forms.Button buttonAdd;
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/AcmDemo/AcmPanel.cs b/NAudioDemo/AcmDemo/AcmPanel.cs
new file mode 100644
index 00000000..c6cb9083
--- /dev/null
+++ b/NAudioDemo/AcmDemo/AcmPanel.cs
@@ -0,0 +1,283 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+using NAudio.Wave.Compression;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo
+{
+ public partial class AcmPanel : UserControl
+ {
+ public AcmPanel()
+ {
+ InitializeComponent();
+ }
+
+ private void AcmForm_Load(object sender, EventArgs e)
+ {
+ RefreshDriversList();
+ }
+
+ private void RefreshDriversList()
+ {
+ listBoxAcmDrivers.Items.Clear();
+ foreach (var driver in AcmDriver.EnumerateAcmDrivers())
+ {
+ listBoxAcmDrivers.Items.Add(driver);
+ }
+ }
+
+ private void buttonEncode_Click(object sender, EventArgs args)
+ {
+ try
+ {
+ EncodeFile();
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message, "Error Encoding", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ }
+
+ private void buttonDecode_Click(object sender, EventArgs args)
+ {
+ try
+ {
+ DecodeFile();
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message, "Error Decoding", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ }
+
+ private WaveFormat GetTargetFormat(WaveFormat inputFormat)
+ {
+ WaveFormat outputFormat;
+ string formatDescription;
+ string formatTagDescription;
+ AcmDriver.ShowFormatChooseDialog(
+ this.Handle,
+ "Select Compressed Format:",
+ AcmFormatEnumFlags.Convert,
+ inputFormat,
+ out outputFormat,
+ out formatDescription,
+ out formatTagDescription);
+ return outputFormat;
+ }
+
+ private void EncodeFile()
+ {
+ string inputFileName = GetInputFileName("Select PCM WAV File to Encode");
+ if (inputFileName == null)
+ return;
+ using (var reader = new WaveFileReader(inputFileName))
+ {
+ if (reader.WaveFormat.Encoding != WaveFormatEncoding.Pcm)
+ {
+ MessageBox.Show("Please select a PCM WAV file to encode");
+ return;
+ }
+ WaveFormat targetFormat = GetTargetFormat(reader.WaveFormat);
+ if (targetFormat == null)
+ {
+ return;
+ }
+ string outputFileName = GetOutputFileName("Select Ouput File Name");
+ if (outputFileName == null)
+ {
+ return;
+ }
+ using (WaveStream convertedStream = new WaveFormatConversionStream(targetFormat, reader))
+ {
+ WaveFileWriter.CreateWaveFile(outputFileName, convertedStream);
+ }
+ if (checkBoxAutoLaunchConvertedFile.Checked)
+ {
+ System.Diagnostics.Process.Start(outputFileName);
+ }
+ }
+ }
+ private void DecodeFile()
+ {
+ string inputFileName = GetInputFileName("Select a compressed WAV File to decode");
+ if (inputFileName == null)
+ return;
+ using (var reader = new WaveFileReader(inputFileName))
+ {
+ if (reader.WaveFormat.Encoding == WaveFormatEncoding.Pcm)
+ {
+ MessageBox.Show("Please select a compressed WAV file to decode");
+ return;
+ }
+ string outputFileName = GetOutputFileName("Select Output File Name");
+ if (outputFileName == null)
+ {
+ return;
+ }
+ using (var convertedStream = WaveFormatConversionStream.CreatePcmStream(reader))
+ {
+ WaveFileWriter.CreateWaveFile(outputFileName, convertedStream);
+ }
+ if (checkBoxAutoLaunchConvertedFile.Checked)
+ {
+ System.Diagnostics.Process.Start(outputFileName);
+ }
+ }
+ }
+
+ private string SelectFileToOpen(string title, string filter)
+ {
+ var openFileDialog = new OpenFileDialog();
+ openFileDialog.Filter = filter;
+ openFileDialog.Title = title;
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ return openFileDialog.FileName;
+ }
+ return null;
+ }
+
+ private string GetInputFileName(string title)
+ {
+ return SelectFileToOpen(title, "WAV File (*.wav)|*.wav");
+ }
+
+ private string GetOutputFileName(string title)
+ {
+ var saveFileDialog = new SaveFileDialog();
+ saveFileDialog.Filter = "WAV File (*.wav)|*.wav";
+ saveFileDialog.Title = title;
+ if (saveFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ return saveFileDialog.FileName;
+ }
+ return null;
+ }
+
+ private void listBoxAcmDrivers_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ var driver = listBoxAcmDrivers.SelectedItem as AcmDriver;
+ richTextBox1.Text = DescribeCodec(driver);
+ }
+
+ private void buttonChooseFormat_Click(object sender, EventArgs e)
+ {
+ WaveFormat selectedFormat;
+ string selectedFormatDescription;
+ string selectedFormatTagDescription;
+ if(AcmDriver.ShowFormatChooseDialog(this.Handle,"Choose a WaveFormat",AcmFormatEnumFlags.None,
+ null,out selectedFormat,
+ out selectedFormatDescription, out selectedFormatTagDescription))
+ {
+ MessageBox.Show(String.Format("{0}\r\n{1}\r\n{2}",
+ selectedFormatDescription,
+ selectedFormatTagDescription,
+ selectedFormat));
+ }
+ }
+
+ private string DescribeCodec(AcmDriver driver)
+ {
+ try
+ {
+ driver.Open();
+ var builder = new StringBuilder();
+ DescribeAcmDriver(driver, builder);
+ foreach (var formatTag in driver.FormatTags)
+ {
+ DescribeAcmFormatTag(builder, formatTag);
+ foreach (var format in driver.GetFormats(formatTag))
+ {
+ DescribeAcmFormat(builder, format);
+ }
+ }
+ driver.Close();
+ return builder.ToString();
+ }
+ catch (Exception e)
+ {
+ return e.ToString();
+ }
+ }
+
+ private static void DescribeAcmDriver(AcmDriver driver, StringBuilder builder)
+ {
+ builder.AppendFormat("Long Name: {0}\r\n", driver.LongName);
+ builder.AppendFormat("Short Name: {0}\r\n", driver.ShortName);
+ builder.AppendFormat("Driver ID: {0}\r\n", driver.DriverId);
+ builder.AppendFormat("FormatTags:\r\n");
+ }
+
+ private static void DescribeAcmFormatTag(StringBuilder builder, AcmFormatTag formatTag)
+ {
+ builder.AppendFormat("===========================================\r\n");
+ builder.AppendFormat("Format Tag {0}: {1}\r\n", formatTag.FormatTagIndex, formatTag.FormatDescription);
+ builder.AppendFormat(" Standard Format Count: {0}\r\n", formatTag.StandardFormatsCount);
+ builder.AppendFormat(" Support Flags: {0}\r\n", formatTag.SupportFlags);
+ builder.AppendFormat(" Format Tag: {0}, Format Size: {1}\r\n", formatTag.FormatTag, formatTag.FormatSize);
+ builder.AppendFormat(" Formats:\r\n");
+ }
+
+ private static void DescribeAcmFormat(StringBuilder builder, AcmFormat format)
+ {
+ builder.AppendFormat(" ===========================================\r\n");
+ builder.AppendFormat(" Format {0}: {1}\r\n", format.FormatIndex, format.FormatDescription);
+ builder.AppendFormat(" FormatTag: {0}, Support Flags: {1}\r\n", format.FormatTag, format.SupportFlags);
+ builder.AppendFormat(
+ " WaveFormat: {0} {1}Hz Channels: {2} Bits: {3} Block Align: {4}, AverageBytesPerSecond: {5} ({6:0.0} kbps), Extra Size: {7}\r\n",
+ format.WaveFormat.Encoding, format.WaveFormat.SampleRate, format.WaveFormat.Channels,
+ format.WaveFormat.BitsPerSample, format.WaveFormat.BlockAlign, format.WaveFormat.AverageBytesPerSecond,
+ (format.WaveFormat.AverageBytesPerSecond*8)/1000.0,
+ format.WaveFormat.ExtraSize);
+ if (format.WaveFormat is WaveFormatExtraData && format.WaveFormat.ExtraSize > 0)
+ {
+ var wfed = (WaveFormatExtraData) format.WaveFormat;
+ builder.Append(" Extra Bytes:\r\n ");
+ for (int n = 0; n < format.WaveFormat.ExtraSize; n++)
+ {
+ builder.AppendFormat("{0:X2} ", wfed.ExtraData[n]);
+ }
+ builder.Append("\r\n");
+ }
+ }
+
+ private void buttonAdd_Click(object sender, EventArgs args)
+ {
+ var codecFile = SelectFileToOpen("Select file to open", "ACM Codecs|*.acm;*.dll");
+ if (codecFile != null)
+ {
+ try
+ {
+ var driver = AcmDriver.AddLocalDriver(codecFile);
+ listBoxAcmDrivers.Items.Add(driver);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message, "Error Adding Driver", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ }
+ }
+ }
+ }
+
+
+ [Export(typeof(INAudioDemoPlugin))]
+ public class AcmPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "ACM Format Conversion"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new AcmPanel();
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/AcmDemo/AcmPanel.resx b/NAudioDemo/AcmDemo/AcmPanel.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/NAudioDemo/AcmDemo/AcmPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/AsioDirectDemo/AsioDirectPanel.Designer.cs b/NAudioDemo/AsioDirectDemo/AsioDirectPanel.Designer.cs
new file mode 100644
index 00000000..f9551fc4
--- /dev/null
+++ b/NAudioDemo/AsioDirectDemo/AsioDirectPanel.Designer.cs
@@ -0,0 +1,143 @@
+namespace NAudioDemo
+{
+ partial class AsioDirectPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AsioDirectPanel));
+ this.label1 = new System.Windows.Forms.Label();
+ this.buttonSelectFile = new System.Windows.Forms.Button();
+ this.buttonPlay = new System.Windows.Forms.Button();
+ this.buttonStop = new System.Windows.Forms.Button();
+ this.comboBoxAsioDevice = new System.Windows.Forms.ComboBox();
+ this.textBoxChannelOffset = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label1.Location = new System.Drawing.Point(4, 4);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(561, 33);
+ this.label1.TabIndex = 0;
+ this.label1.Text = resources.GetString("label1.Text");
+ //
+ // buttonSelectFile
+ //
+ this.buttonSelectFile.Location = new System.Drawing.Point(7, 41);
+ this.buttonSelectFile.Name = "buttonSelectFile";
+ this.buttonSelectFile.Size = new System.Drawing.Size(105, 23);
+ this.buttonSelectFile.TabIndex = 1;
+ this.buttonSelectFile.Text = "Open WAV...";
+ this.buttonSelectFile.UseVisualStyleBackColor = true;
+ this.buttonSelectFile.Click += new System.EventHandler(this.buttonSelectFile_Click);
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.Location = new System.Drawing.Point(409, 40);
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(75, 23);
+ this.buttonPlay.TabIndex = 2;
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.UseVisualStyleBackColor = true;
+ this.buttonPlay.Click += new System.EventHandler(this.buttonPlay_Click);
+ //
+ // buttonStop
+ //
+ this.buttonStop.Location = new System.Drawing.Point(490, 40);
+ this.buttonStop.Name = "buttonStop";
+ this.buttonStop.Size = new System.Drawing.Size(75, 23);
+ this.buttonStop.TabIndex = 2;
+ this.buttonStop.Text = "Stop";
+ this.buttonStop.UseVisualStyleBackColor = true;
+ this.buttonStop.Click += new System.EventHandler(this.buttonStop_Click);
+ //
+ // comboBoxAsioDevice
+ //
+ this.comboBoxAsioDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxAsioDevice.FormattingEnabled = true;
+ this.comboBoxAsioDevice.Location = new System.Drawing.Point(118, 42);
+ this.comboBoxAsioDevice.Name = "comboBoxAsioDevice";
+ this.comboBoxAsioDevice.Size = new System.Drawing.Size(285, 21);
+ this.comboBoxAsioDevice.TabIndex = 3;
+ //
+ // textBoxChannelOffset
+ //
+ this.textBoxChannelOffset.Location = new System.Drawing.Point(100, 75);
+ this.textBoxChannelOffset.Name = "textBoxChannelOffset";
+ this.textBoxChannelOffset.Size = new System.Drawing.Size(46, 20);
+ this.textBoxChannelOffset.TabIndex = 4;
+ this.textBoxChannelOffset.Text = "0";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(14, 78);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(80, 13);
+ this.label2.TabIndex = 5;
+ this.label2.Text = "Channel Offset:";
+ //
+ // timer1
+ //
+ this.timer1.Interval = 250;
+ this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // AsioDirectPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.textBoxChannelOffset);
+ this.Controls.Add(this.comboBoxAsioDevice);
+ this.Controls.Add(this.buttonStop);
+ this.Controls.Add(this.buttonPlay);
+ this.Controls.Add(this.buttonSelectFile);
+ this.Controls.Add(this.label1);
+ this.Name = "AsioDirectPanel";
+ this.Size = new System.Drawing.Size(568, 150);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button buttonSelectFile;
+ private System.Windows.Forms.Button buttonPlay;
+ private System.Windows.Forms.Button buttonStop;
+ private System.Windows.Forms.ComboBox comboBoxAsioDevice;
+ private System.Windows.Forms.TextBox textBoxChannelOffset;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Timer timer1;
+ }
+}
diff --git a/NAudioDemo/AsioDirectDemo/AsioDirectPanel.cs b/NAudioDemo/AsioDirectDemo/AsioDirectPanel.cs
new file mode 100644
index 00000000..7ebdb9aa
--- /dev/null
+++ b/NAudioDemo/AsioDirectDemo/AsioDirectPanel.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo
+{
+ public partial class AsioDirectPanel : UserControl
+ {
+ private AudioFileReader reader;
+ private AsioOut asioOut;
+
+ public AsioDirectPanel()
+ {
+ InitializeComponent();
+ this.Disposed += new EventHandler(AsioDirectPanel_Disposed);
+ foreach(var device in AsioOut.GetDriverNames())
+ {
+ this.comboBoxAsioDevice.Items.Add(device);
+ }
+ if (this.comboBoxAsioDevice.Items.Count > 0)
+ {
+ this.comboBoxAsioDevice.SelectedIndex = 0;
+ }
+ }
+
+ void AsioDirectPanel_Disposed(object sender, EventArgs e)
+ {
+ Cleanup();
+ }
+
+ private void Cleanup()
+ {
+ if (this.asioOut != null)
+ {
+ this.asioOut.Dispose();
+ this.asioOut = null;
+ }
+ if (this.reader != null)
+ {
+ this.reader.Dispose();
+ this.reader = null;
+ }
+ }
+
+ private void buttonSelectFile_Click(object sender, EventArgs e)
+ {
+ Cleanup();
+ var ofd = new OpenFileDialog();
+ ofd.Filter = "Audio files|*.wav;*.mp3";
+ if (ofd.ShowDialog() == DialogResult.OK)
+ {
+ this.reader = new AudioFileReader(ofd.FileName);
+ }
+ }
+
+ private void buttonPlay_Click(object sender, EventArgs args)
+ {
+ try
+ {
+ Play();
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message);
+ }
+ }
+
+ private int GetUserSpecifiedChannelOffset()
+ {
+ int channelOffset = 0;
+ int.TryParse(textBoxChannelOffset.Text, out channelOffset);
+ return channelOffset;
+ }
+
+ private void Play()
+ {
+ // allow change device
+ if (this.asioOut != null &&
+ (this.asioOut.DriverName != comboBoxAsioDevice.Text ||
+ this.asioOut.ChannelOffset != GetUserSpecifiedChannelOffset()))
+ {
+ this.asioOut.Dispose();
+ this.asioOut = null;
+ }
+
+ // create device if necessary
+ if (this.asioOut == null)
+ {
+ this.asioOut = new AsioOut(comboBoxAsioDevice.Text);
+ this.asioOut.ChannelOffset = GetUserSpecifiedChannelOffset();
+ this.asioOut.Init(this.reader);
+ }
+
+ this.reader.Position = 0;
+ this.asioOut.Play();
+ this.timer1.Enabled = true;
+ SetButtonStates();
+ }
+
+ private void SetButtonStates()
+ {
+ buttonPlay.Enabled = asioOut != null && asioOut.PlaybackState != PlaybackState.Playing;
+ buttonStop.Enabled = asioOut != null && asioOut.PlaybackState == PlaybackState.Playing;
+ }
+
+ private void buttonStop_Click(object sender, EventArgs e)
+ {
+ Stop();
+ }
+
+ private void Stop()
+ {
+ this.asioOut.Stop();
+ this.timer1.Enabled = false;
+ SetButtonStates();
+ }
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ if (asioOut != null && asioOut.PlaybackState == PlaybackState.Playing && reader.Position >= reader.Length)
+ {
+ Stop();
+ }
+ }
+ }
+
+ [Export(typeof(INAudioDemoPlugin))]
+ public class AsioDirectPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "ASIO Direct Playback"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new AsioDirectPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/AsioDirectDemo/AsioDirectPanel.resx b/NAudioDemo/AsioDirectDemo/AsioDirectPanel.resx
new file mode 100644
index 00000000..12712f7b
--- /dev/null
+++ b/NAudioDemo/AsioDirectDemo/AsioDirectPanel.resx
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ This is for testing ASIO with various input formats. Normally you should pass in 32 bit float (see the more fully featured playback demo). There is no volume control so be careful your speakers aren't up too loud.
+
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.Designer.cs b/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.Designer.cs
new file mode 100644
index 00000000..2e6f69ac
--- /dev/null
+++ b/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.Designer.cs
@@ -0,0 +1,226 @@
+namespace NAudioDemo
+{
+ partial class AsioRecordingPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.label1 = new System.Windows.Forms.Label();
+ this.buttonStart = new System.Windows.Forms.Button();
+ this.buttonStop = new System.Windows.Forms.Button();
+ this.comboBoxAsioDevice = new System.Windows.Forms.ComboBox();
+ this.textBoxChannelOffset = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.textBoxChannelCount = new System.Windows.Forms.TextBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.listBoxRecordings = new System.Windows.Forms.ListBox();
+ this.buttonPlay = new System.Windows.Forms.Button();
+ this.buttonDelete = new System.Windows.Forms.Button();
+ this.buttonControlPanel = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label1.Location = new System.Drawing.Point(4, 4);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(561, 33);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "This is for testing ASIO Recording";
+ //
+ // buttonStart
+ //
+ this.buttonStart.Location = new System.Drawing.Point(409, 40);
+ this.buttonStart.Name = "buttonStart";
+ this.buttonStart.Size = new System.Drawing.Size(75, 23);
+ this.buttonStart.TabIndex = 2;
+ this.buttonStart.Text = "Start";
+ this.buttonStart.UseVisualStyleBackColor = true;
+ this.buttonStart.Click += new System.EventHandler(this.buttonStart_Click);
+ //
+ // buttonStop
+ //
+ this.buttonStop.Location = new System.Drawing.Point(490, 40);
+ this.buttonStop.Name = "buttonStop";
+ this.buttonStop.Size = new System.Drawing.Size(75, 23);
+ this.buttonStop.TabIndex = 2;
+ this.buttonStop.Text = "Stop";
+ this.buttonStop.UseVisualStyleBackColor = true;
+ this.buttonStop.Click += new System.EventHandler(this.buttonStop_Click);
+ //
+ // comboBoxAsioDevice
+ //
+ this.comboBoxAsioDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxAsioDevice.FormattingEnabled = true;
+ this.comboBoxAsioDevice.Location = new System.Drawing.Point(118, 42);
+ this.comboBoxAsioDevice.Name = "comboBoxAsioDevice";
+ this.comboBoxAsioDevice.Size = new System.Drawing.Size(285, 21);
+ this.comboBoxAsioDevice.TabIndex = 3;
+ //
+ // textBoxChannelOffset
+ //
+ this.textBoxChannelOffset.Location = new System.Drawing.Point(100, 104);
+ this.textBoxChannelOffset.Name = "textBoxChannelOffset";
+ this.textBoxChannelOffset.Size = new System.Drawing.Size(46, 20);
+ this.textBoxChannelOffset.TabIndex = 4;
+ this.textBoxChannelOffset.Text = "0";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(14, 107);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(80, 13);
+ this.label2.TabIndex = 5;
+ this.label2.Text = "Channel Offset:";
+ //
+ // timer1
+ //
+ this.timer1.Interval = 250;
+ this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // textBoxChannelCount
+ //
+ this.textBoxChannelCount.Location = new System.Drawing.Point(100, 74);
+ this.textBoxChannelCount.Name = "textBoxChannelCount";
+ this.textBoxChannelCount.Size = new System.Drawing.Size(46, 20);
+ this.textBoxChannelCount.TabIndex = 4;
+ this.textBoxChannelCount.Text = "1";
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(14, 77);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(80, 13);
+ this.label3.TabIndex = 5;
+ this.label3.Text = "Channel Count:";
+ //
+ // label4
+ //
+ this.label4.Location = new System.Drawing.Point(152, 77);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(413, 30);
+ this.label4.TabIndex = 5;
+ this.label4.Text = "This is the number of channels to record. Can\'t be set to more inputs than your d" +
+ "evice has available";
+ //
+ // label5
+ //
+ this.label5.Location = new System.Drawing.Point(152, 107);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(413, 30);
+ this.label5.TabIndex = 5;
+ this.label5.Text = "Use channel offset to skip over a number of input channels and select the one you" +
+ " want (normally channel count would be set to 1 in this case)";
+ //
+ // listBoxRecordings
+ //
+ this.listBoxRecordings.FormattingEnabled = true;
+ this.listBoxRecordings.Location = new System.Drawing.Point(17, 144);
+ this.listBoxRecordings.Name = "listBoxRecordings";
+ this.listBoxRecordings.Size = new System.Drawing.Size(424, 95);
+ this.listBoxRecordings.TabIndex = 6;
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.Location = new System.Drawing.Point(448, 144);
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(75, 23);
+ this.buttonPlay.TabIndex = 7;
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.UseVisualStyleBackColor = true;
+ this.buttonPlay.Click += new System.EventHandler(this.buttonPlay_Click);
+ //
+ // buttonDelete
+ //
+ this.buttonDelete.Location = new System.Drawing.Point(448, 173);
+ this.buttonDelete.Name = "buttonDelete";
+ this.buttonDelete.Size = new System.Drawing.Size(75, 23);
+ this.buttonDelete.TabIndex = 7;
+ this.buttonDelete.Text = "Delete";
+ this.buttonDelete.UseVisualStyleBackColor = true;
+ this.buttonDelete.Click += new System.EventHandler(this.buttonDelete_Click);
+ //
+ // buttonControlPanel
+ //
+ this.buttonControlPanel.Location = new System.Drawing.Point(17, 40);
+ this.buttonControlPanel.Name = "buttonControlPanel";
+ this.buttonControlPanel.Size = new System.Drawing.Size(95, 23);
+ this.buttonControlPanel.TabIndex = 2;
+ this.buttonControlPanel.Text = "Control Panel";
+ this.buttonControlPanel.UseVisualStyleBackColor = true;
+ this.buttonControlPanel.Click += new System.EventHandler(this.buttonControlPanel_Click);
+ //
+ // AsioRecordingPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.buttonDelete);
+ this.Controls.Add(this.buttonPlay);
+ this.Controls.Add(this.listBoxRecordings);
+ this.Controls.Add(this.label5);
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.textBoxChannelCount);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.textBoxChannelOffset);
+ this.Controls.Add(this.comboBoxAsioDevice);
+ this.Controls.Add(this.buttonStop);
+ this.Controls.Add(this.buttonControlPanel);
+ this.Controls.Add(this.buttonStart);
+ this.Controls.Add(this.label1);
+ this.Name = "AsioRecordingPanel";
+ this.Size = new System.Drawing.Size(568, 253);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button buttonStart;
+ private System.Windows.Forms.Button buttonStop;
+ private System.Windows.Forms.ComboBox comboBoxAsioDevice;
+ private System.Windows.Forms.TextBox textBoxChannelOffset;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.TextBox textBoxChannelCount;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.ListBox listBoxRecordings;
+ private System.Windows.Forms.Button buttonPlay;
+ private System.Windows.Forms.Button buttonDelete;
+ private System.Windows.Forms.Button buttonControlPanel;
+ }
+}
diff --git a/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.cs b/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.cs
new file mode 100644
index 00000000..1ef07337
--- /dev/null
+++ b/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Diagnostics;
+
+namespace NAudioDemo
+{
+ public partial class AsioRecordingPanel : UserControl
+ {
+ private WaveFileWriter writer;
+ private AsioOut asioOut;
+ private string fileName;
+
+ public AsioRecordingPanel()
+ {
+ InitializeComponent();
+ this.Disposed += new EventHandler(AsioDirectPanel_Disposed);
+ foreach(var device in AsioOut.GetDriverNames())
+ {
+ this.comboBoxAsioDevice.Items.Add(device);
+ }
+ if (this.comboBoxAsioDevice.Items.Count > 0)
+ {
+ this.comboBoxAsioDevice.SelectedIndex = 0;
+ }
+ }
+
+ void AsioDirectPanel_Disposed(object sender, EventArgs e)
+ {
+ Cleanup();
+ }
+
+ private void Cleanup()
+ {
+ if (this.asioOut != null)
+ {
+ this.asioOut.Dispose();
+ this.asioOut = null;
+ }
+ if (this.writer != null)
+ {
+ this.writer.Dispose();
+ this.writer = null;
+ }
+ }
+
+ private void buttonStart_Click(object sender, EventArgs args)
+ {
+ try
+ {
+ Start();
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message);
+ }
+ }
+
+ private int GetUserSpecifiedChannelOffset()
+ {
+ int channelOffset = 0;
+ int.TryParse(textBoxChannelOffset.Text, out channelOffset);
+ return channelOffset;
+ }
+
+ private int GetUserSpecifiedChannelCount()
+ {
+ int channelCount = 1;
+ int.TryParse(textBoxChannelCount.Text, out channelCount);
+ return channelCount;
+ }
+
+ private void Start()
+ {
+ // allow change device
+ if (this.asioOut != null &&
+ (this.asioOut.DriverName != comboBoxAsioDevice.Text ||
+ this.asioOut.ChannelOffset != GetUserSpecifiedChannelOffset()))
+ {
+ this.asioOut.AudioAvailable -= asioOut_AudioAvailable;
+ this.asioOut.Dispose();
+ this.asioOut = null;
+ }
+
+ int recordChannelCount = GetUserSpecifiedChannelCount();
+
+ // create device if necessary
+ if (this.asioOut == null)
+ {
+ this.asioOut = new AsioOut(comboBoxAsioDevice.Text);
+ this.asioOut.InputChannelOffset = GetUserSpecifiedChannelOffset();
+ this.asioOut.InitRecordAndPlayback(null, recordChannelCount, 44100);
+ this.asioOut.AudioAvailable += asioOut_AudioAvailable;
+ }
+
+ this.fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".wav");
+ this.writer = new WaveFileWriter(fileName, new WaveFormat(44100, recordChannelCount));
+ this.asioOut.Play();
+ this.timer1.Enabled = true;
+ SetButtonStates();
+ }
+
+ void asioOut_AudioAvailable(object sender, AsioAudioAvailableEventArgs e)
+ {
+ var samples = e.GetAsInterleavedSamples();
+ writer.WriteSamples(samples, 0, samples.Length);
+ }
+
+ private void SetButtonStates()
+ {
+ buttonStart.Enabled = asioOut != null && asioOut.PlaybackState != PlaybackState.Playing;
+ buttonStop.Enabled = asioOut != null && asioOut.PlaybackState == PlaybackState.Playing;
+ }
+
+ private void buttonStop_Click(object sender, EventArgs e)
+ {
+ Stop();
+ }
+
+ private void Stop()
+ {
+ this.asioOut.Stop();
+ if (this.writer != null)
+ {
+ this.writer.Dispose();
+ this.writer = null;
+ }
+ this.timer1.Enabled = false;
+ SetButtonStates();
+ int index = listBoxRecordings.Items.Add(fileName);
+ listBoxRecordings.SelectedIndex = index;
+ }
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ if (asioOut != null && asioOut.PlaybackState == PlaybackState.Playing && writer.Length > writer.WaveFormat.AverageBytesPerSecond * 30)
+ {
+ Stop();
+ }
+ }
+
+ private void buttonPlay_Click(object sender, EventArgs e)
+ {
+ if (listBoxRecordings.SelectedItem != null)
+ {
+ Process.Start((string)listBoxRecordings.SelectedItem);
+ }
+ }
+
+ private void buttonDelete_Click(object sender, EventArgs e)
+ {
+ if (listBoxRecordings.SelectedItem != null)
+ {
+ File.Delete((string)listBoxRecordings.SelectedItem);
+ listBoxRecordings.Items.Remove(listBoxRecordings.SelectedItem);
+ }
+ }
+
+ public string SelectedDeviceName { get { return (string)comboBoxAsioDevice.SelectedItem; } }
+
+ private void buttonControlPanel_Click(object sender, EventArgs args)
+ {
+ try
+ {
+ using (var asio = new AsioOut(SelectedDeviceName))
+ {
+ asio.ShowControlPanel();
+ }
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message);
+ }
+ }
+ }
+
+ [Export(typeof(INAudioDemoPlugin))]
+ public class AsioRecordingPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "ASIO Recording"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new AsioRecordingPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.resx b/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.resx
new file mode 100644
index 00000000..6999fbfa
--- /dev/null
+++ b/NAudioDemo/AsioRecordingDemo/AsioRecordingPanel.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/NAudioDemo/AudioPlaybackDemo/AsioOutPlugin.cs b/NAudioDemo/AudioPlaybackDemo/AsioOutPlugin.cs
new file mode 100644
index 00000000..adae09a2
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AsioOutPlugin.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+using System.Windows.Forms;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ [Export(typeof(IOutputDevicePlugin))]
+ class AsioOutPlugin : IOutputDevicePlugin
+ {
+ AsioOutSettingsPanel settingsPanel;
+
+ public IWavePlayer CreateDevice(int latency)
+ {
+ return new AsioOut(settingsPanel.SelectedDeviceName);
+ }
+
+ public UserControl CreateSettingsPanel()
+ {
+ this.settingsPanel = new AsioOutSettingsPanel();
+ return settingsPanel;
+ }
+
+ public string Name
+ {
+ get { return "AsioOut"; }
+ }
+
+ public bool IsAvailable
+ {
+ get { return AsioOut.isSupported(); }
+ }
+
+ public int Priority
+ {
+ get { return 4; }
+ }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.Designer.cs b/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.Designer.cs
new file mode 100644
index 00000000..0ef348ed
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.Designer.cs
@@ -0,0 +1,71 @@
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ partial class AsioOutSettingsPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.comboBoxAsioDriver = new System.Windows.Forms.ComboBox();
+ this.buttonControlPanel = new System.Windows.Forms.Button();
+ this.SuspendLayout();
+ //
+ // comboBoxAsioDriver
+ //
+ this.comboBoxAsioDriver.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxAsioDriver.FormattingEnabled = true;
+ this.comboBoxAsioDriver.Location = new System.Drawing.Point(3, 3);
+ this.comboBoxAsioDriver.Name = "comboBoxAsioDriver";
+ this.comboBoxAsioDriver.Size = new System.Drawing.Size(232, 21);
+ this.comboBoxAsioDriver.TabIndex = 18;
+ //
+ // buttonControlPanel
+ //
+ this.buttonControlPanel.Location = new System.Drawing.Point(3, 30);
+ this.buttonControlPanel.Name = "buttonControlPanel";
+ this.buttonControlPanel.Size = new System.Drawing.Size(135, 23);
+ this.buttonControlPanel.TabIndex = 17;
+ this.buttonControlPanel.Text = "ASIO Control Panel";
+ this.buttonControlPanel.UseVisualStyleBackColor = true;
+ this.buttonControlPanel.Click += new System.EventHandler(this.buttonControlPanel_Click);
+ //
+ // AsioOutSettingsPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.comboBoxAsioDriver);
+ this.Controls.Add(this.buttonControlPanel);
+ this.Name = "AsioOutSettingsPanel";
+ this.Size = new System.Drawing.Size(242, 62);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ComboBox comboBoxAsioDriver;
+ private System.Windows.Forms.Button buttonControlPanel;
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.cs b/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.cs
new file mode 100644
index 00000000..4d4bfa9e
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ public partial class AsioOutSettingsPanel : UserControl
+ {
+ public AsioOutSettingsPanel()
+ {
+ InitializeComponent();
+ InitialiseAsioControls();
+ }
+
+ private void InitialiseAsioControls()
+ {
+ // Just fill the comboBox AsioDriver with available driver names
+ var asioDriverNames = AsioOut.GetDriverNames();
+ foreach (string driverName in asioDriverNames)
+ {
+ comboBoxAsioDriver.Items.Add(driverName);
+ }
+ comboBoxAsioDriver.SelectedIndex = 0;
+ }
+
+ public string SelectedDeviceName { get { return (string)comboBoxAsioDriver.SelectedItem; } }
+
+ private void buttonControlPanel_Click(object sender, EventArgs args)
+ {
+ try
+ {
+ using (var asio = new AsioOut(SelectedDeviceName))
+ {
+ asio.ShowControlPanel();
+ }
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(e.Message);
+ }
+ }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.resx b/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.resx
new file mode 100644
index 00000000..5ea0895e
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AsioOutSettingsPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.Designer.cs b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.Designer.cs
new file mode 100644
index 00000000..4c3c7b0d
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.Designer.cs
@@ -0,0 +1,341 @@
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ partial class AudioPlaybackPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ CloseWaveOut();
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.comboBoxLatency = new System.Windows.Forms.ComboBox();
+ this.groupBoxDriverModel = new System.Windows.Forms.GroupBox();
+ this.panelOutputDeviceSettings = new System.Windows.Forms.Panel();
+ this.comboBoxOutputDevice = new System.Windows.Forms.ComboBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.toolStrip1 = new System.Windows.Forms.ToolStrip();
+ this.toolStripButtonOpenFile = new System.Windows.Forms.ToolStripButton();
+ this.buttonPlay = new System.Windows.Forms.ToolStripButton();
+ this.buttonPause = new System.Windows.Forms.ToolStripButton();
+ this.buttonStop = new System.Windows.Forms.ToolStripButton();
+ this.toolStripLabel1 = new System.Windows.Forms.ToolStripLabel();
+ this.labelCurrentTime = new System.Windows.Forms.ToolStripLabel();
+ this.toolStripLabel3 = new System.Windows.Forms.ToolStripLabel();
+ this.labelTotalTime = new System.Windows.Forms.ToolStripLabel();
+ this.trackBarPosition = new System.Windows.Forms.TrackBar();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.label2 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.waveformPainter2 = new NAudio.Gui.WaveformPainter();
+ this.waveformPainter1 = new NAudio.Gui.WaveformPainter();
+ this.volumeMeter2 = new NAudio.Gui.VolumeMeter();
+ this.volumeMeter1 = new NAudio.Gui.VolumeMeter();
+ this.volumeSlider1 = new NAudio.Gui.VolumeSlider();
+ this.groupBoxDriverModel.SuspendLayout();
+ this.toolStrip1.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarPosition)).BeginInit();
+ this.SuspendLayout();
+ //
+ // comboBoxLatency
+ //
+ this.comboBoxLatency.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.comboBoxLatency.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxLatency.FormattingEnabled = true;
+ this.comboBoxLatency.Location = new System.Drawing.Point(442, 27);
+ this.comboBoxLatency.Name = "comboBoxLatency";
+ this.comboBoxLatency.Size = new System.Drawing.Size(75, 21);
+ this.comboBoxLatency.TabIndex = 10;
+ //
+ // groupBoxDriverModel
+ //
+ this.groupBoxDriverModel.Controls.Add(this.panelOutputDeviceSettings);
+ this.groupBoxDriverModel.Controls.Add(this.comboBoxOutputDevice);
+ this.groupBoxDriverModel.Location = new System.Drawing.Point(12, 30);
+ this.groupBoxDriverModel.Name = "groupBoxDriverModel";
+ this.groupBoxDriverModel.Size = new System.Drawing.Size(263, 288);
+ this.groupBoxDriverModel.TabIndex = 13;
+ this.groupBoxDriverModel.TabStop = false;
+ this.groupBoxDriverModel.Text = "Output Driver";
+ //
+ // panelOutputDeviceSettings
+ //
+ this.panelOutputDeviceSettings.Location = new System.Drawing.Point(7, 52);
+ this.panelOutputDeviceSettings.Name = "panelOutputDeviceSettings";
+ this.panelOutputDeviceSettings.Size = new System.Drawing.Size(250, 236);
+ this.panelOutputDeviceSettings.TabIndex = 1;
+ //
+ // comboBoxOutputDevice
+ //
+ this.comboBoxOutputDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxOutputDevice.FormattingEnabled = true;
+ this.comboBoxOutputDevice.Location = new System.Drawing.Point(6, 24);
+ this.comboBoxOutputDevice.Name = "comboBoxOutputDevice";
+ this.comboBoxOutputDevice.Size = new System.Drawing.Size(251, 21);
+ this.comboBoxOutputDevice.TabIndex = 0;
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(523, 30);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(20, 13);
+ this.label1.TabIndex = 13;
+ this.label1.Text = "ms";
+ //
+ // toolStrip1
+ //
+ this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.toolStripButtonOpenFile,
+ this.buttonPlay,
+ this.buttonPause,
+ this.buttonStop,
+ this.toolStripLabel1,
+ this.labelCurrentTime,
+ this.toolStripLabel3,
+ this.labelTotalTime});
+ this.toolStrip1.Location = new System.Drawing.Point(0, 0);
+ this.toolStrip1.Name = "toolStrip1";
+ this.toolStrip1.Size = new System.Drawing.Size(591, 25);
+ this.toolStrip1.TabIndex = 15;
+ this.toolStrip1.Text = "toolStrip1";
+ //
+ // toolStripButtonOpenFile
+ //
+ this.toolStripButtonOpenFile.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.toolStripButtonOpenFile.Image = global::NAudioDemo.Images.Open;
+ this.toolStripButtonOpenFile.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.toolStripButtonOpenFile.Name = "toolStripButtonOpenFile";
+ this.toolStripButtonOpenFile.Size = new System.Drawing.Size(23, 22);
+ this.toolStripButtonOpenFile.Text = "Open File";
+ this.toolStripButtonOpenFile.Click += new System.EventHandler(this.OnOpenFileClick);
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.buttonPlay.Image = global::NAudioDemo.Images.Play;
+ this.buttonPlay.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(23, 22);
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.Click += new System.EventHandler(this.OnButtonPlayClick);
+ //
+ // buttonPause
+ //
+ this.buttonPause.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.buttonPause.Image = global::NAudioDemo.Images.Pause;
+ this.buttonPause.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.buttonPause.Name = "buttonPause";
+ this.buttonPause.Size = new System.Drawing.Size(23, 22);
+ this.buttonPause.Text = "Pause";
+ this.buttonPause.Click += new System.EventHandler(this.OnButtonPauseClick);
+ //
+ // buttonStop
+ //
+ this.buttonStop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
+ this.buttonStop.Image = global::NAudioDemo.Images.Stop;
+ this.buttonStop.ImageTransparentColor = System.Drawing.Color.Magenta;
+ this.buttonStop.Name = "buttonStop";
+ this.buttonStop.Size = new System.Drawing.Size(23, 22);
+ this.buttonStop.Text = "Stop";
+ this.buttonStop.Click += new System.EventHandler(this.OnButtonStopClick);
+ //
+ // toolStripLabel1
+ //
+ this.toolStripLabel1.Name = "toolStripLabel1";
+ this.toolStripLabel1.Size = new System.Drawing.Size(80, 22);
+ this.toolStripLabel1.Text = "Current Time:";
+ //
+ // labelCurrentTime
+ //
+ this.labelCurrentTime.Name = "labelCurrentTime";
+ this.labelCurrentTime.Size = new System.Drawing.Size(34, 22);
+ this.labelCurrentTime.Text = "00:00";
+ //
+ // toolStripLabel3
+ //
+ this.toolStripLabel3.Name = "toolStripLabel3";
+ this.toolStripLabel3.Size = new System.Drawing.Size(67, 22);
+ this.toolStripLabel3.Text = "Total Time:";
+ //
+ // labelTotalTime
+ //
+ this.labelTotalTime.Name = "labelTotalTime";
+ this.labelTotalTime.Size = new System.Drawing.Size(34, 22);
+ this.labelTotalTime.Text = "00:00";
+ //
+ // trackBarPosition
+ //
+ this.trackBarPosition.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.trackBarPosition.LargeChange = 10;
+ this.trackBarPosition.Location = new System.Drawing.Point(18, 324);
+ this.trackBarPosition.Maximum = 100;
+ this.trackBarPosition.Name = "trackBarPosition";
+ this.trackBarPosition.Size = new System.Drawing.Size(569, 45);
+ this.trackBarPosition.TabIndex = 16;
+ this.trackBarPosition.TickFrequency = 5;
+ this.trackBarPosition.Scroll += new System.EventHandler(this.trackBarPosition_Scroll);
+ //
+ // timer1
+ //
+ this.timer1.Enabled = true;
+ this.timer1.Interval = 500;
+ this.timer1.Tick += new System.EventHandler(this.OnTimerTick);
+ //
+ // label2
+ //
+ this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(333, 30);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(103, 13);
+ this.label2.TabIndex = 17;
+ this.label2.Text = "Requested Latency:";
+ //
+ // label3
+ //
+ this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(333, 57);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(45, 13);
+ this.label3.TabIndex = 17;
+ this.label3.Text = "Volume:";
+ //
+ // waveformPainter2
+ //
+ this.waveformPainter2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.waveformPainter2.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(192)))));
+ this.waveformPainter2.ForeColor = System.Drawing.Color.SaddleBrown;
+ this.waveformPainter2.Location = new System.Drawing.Point(281, 205);
+ this.waveformPainter2.Name = "waveformPainter2";
+ this.waveformPainter2.Size = new System.Drawing.Size(300, 60);
+ this.waveformPainter2.TabIndex = 19;
+ this.waveformPainter2.Text = "waveformPainter1";
+ //
+ // waveformPainter1
+ //
+ this.waveformPainter1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.waveformPainter1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(192)))));
+ this.waveformPainter1.ForeColor = System.Drawing.Color.SaddleBrown;
+ this.waveformPainter1.Location = new System.Drawing.Point(281, 141);
+ this.waveformPainter1.Name = "waveformPainter1";
+ this.waveformPainter1.Size = new System.Drawing.Size(300, 60);
+ this.waveformPainter1.TabIndex = 19;
+ this.waveformPainter1.Text = "waveformPainter1";
+ //
+ // volumeMeter2
+ //
+ this.volumeMeter2.Amplitude = 0F;
+ this.volumeMeter2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.volumeMeter2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(192)))), ((int)(((byte)(0)))));
+ this.volumeMeter2.Location = new System.Drawing.Point(567, 30);
+ this.volumeMeter2.MaxDb = 3F;
+ this.volumeMeter2.MinDb = -60F;
+ this.volumeMeter2.Name = "volumeMeter2";
+ this.volumeMeter2.Size = new System.Drawing.Size(14, 102);
+ this.volumeMeter2.TabIndex = 18;
+ this.volumeMeter2.Text = "volumeMeter1";
+ //
+ // volumeMeter1
+ //
+ this.volumeMeter1.Amplitude = 0F;
+ this.volumeMeter1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.volumeMeter1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(192)))), ((int)(((byte)(0)))));
+ this.volumeMeter1.Location = new System.Drawing.Point(549, 30);
+ this.volumeMeter1.MaxDb = 3F;
+ this.volumeMeter1.MinDb = -60F;
+ this.volumeMeter1.Name = "volumeMeter1";
+ this.volumeMeter1.Size = new System.Drawing.Size(14, 102);
+ this.volumeMeter1.TabIndex = 18;
+ this.volumeMeter1.Text = "volumeMeter1";
+ //
+ // volumeSlider1
+ //
+ this.volumeSlider1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.volumeSlider1.Location = new System.Drawing.Point(442, 54);
+ this.volumeSlider1.Name = "volumeSlider1";
+ this.volumeSlider1.Size = new System.Drawing.Size(96, 16);
+ this.volumeSlider1.TabIndex = 11;
+ this.volumeSlider1.VolumeChanged += new System.EventHandler(this.OnVolumeSliderChanged);
+ //
+ // AudioPlaybackPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.waveformPainter2);
+ this.Controls.Add(this.waveformPainter1);
+ this.Controls.Add(this.volumeMeter2);
+ this.Controls.Add(this.volumeMeter1);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.trackBarPosition);
+ this.Controls.Add(this.toolStrip1);
+ this.Controls.Add(this.groupBoxDriverModel);
+ this.Controls.Add(this.comboBoxLatency);
+ this.Controls.Add(this.volumeSlider1);
+ this.Name = "AudioPlaybackPanel";
+ this.Size = new System.Drawing.Size(591, 381);
+ this.Load += new System.EventHandler(this.Form1_Load);
+ this.groupBoxDriverModel.ResumeLayout(false);
+ this.toolStrip1.ResumeLayout(false);
+ this.toolStrip1.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.trackBarPosition)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ComboBox comboBoxLatency;
+ private NAudio.Gui.VolumeSlider volumeSlider1;
+ private System.Windows.Forms.GroupBox groupBoxDriverModel;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.ToolStrip toolStrip1;
+ private System.Windows.Forms.ToolStripButton buttonPlay;
+ private System.Windows.Forms.ToolStripButton buttonPause;
+ private System.Windows.Forms.ToolStripButton buttonStop;
+ private System.Windows.Forms.TrackBar trackBarPosition;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.ToolStripButton toolStripButtonOpenFile;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.ToolStripLabel toolStripLabel1;
+ private System.Windows.Forms.ToolStripLabel labelCurrentTime;
+ private System.Windows.Forms.ToolStripLabel toolStripLabel3;
+ private System.Windows.Forms.ToolStripLabel labelTotalTime;
+ private System.Windows.Forms.Label label3;
+ private NAudio.Gui.VolumeMeter volumeMeter1;
+ private NAudio.Gui.VolumeMeter volumeMeter2;
+ private NAudio.Gui.WaveformPainter waveformPainter1;
+ private NAudio.Gui.WaveformPainter waveformPainter2;
+ private System.Windows.Forms.Panel panelOutputDeviceSettings;
+ private System.Windows.Forms.ComboBox comboBoxOutputDevice;
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.cs b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.cs
new file mode 100644
index 00000000..2bad806b
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.cs
@@ -0,0 +1,275 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Windows.Forms;
+using NAudio.Wave;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ [Export]
+ public partial class AudioPlaybackPanel : UserControl
+ {
+ private IWavePlayer waveOut;
+ private string fileName = null;
+ private AudioFileReader audioFileReader;
+ private Action setVolumeDelegate;
+
+ [ImportingConstructor]
+ public AudioPlaybackPanel([ImportMany]IEnumerable outputDevicePlugins)
+ {
+ InitializeComponent();
+ LoadOutputDevicePlugins(outputDevicePlugins);
+ }
+
+ private void LoadOutputDevicePlugins(IEnumerable outputDevicePlugins)
+ {
+ comboBoxOutputDevice.DisplayMember = "Name";
+ comboBoxOutputDevice.SelectedIndexChanged += comboBoxOutputDevice_SelectedIndexChanged;
+ foreach (var outputDevicePlugin in outputDevicePlugins.OrderBy(p => p.Priority))
+ {
+ comboBoxOutputDevice.Items.Add(outputDevicePlugin);
+ }
+ comboBoxOutputDevice.SelectedIndex = 0;
+ }
+
+ void comboBoxOutputDevice_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ panelOutputDeviceSettings.Controls.Clear();
+ Control settingsPanel;
+ if (SelectedOutputDevicePlugin.IsAvailable)
+ {
+ settingsPanel = SelectedOutputDevicePlugin.CreateSettingsPanel();
+ }
+ else
+ {
+ settingsPanel = new Label() { Text = "This output device is unavailable on your system", Dock=DockStyle.Fill };
+ }
+ panelOutputDeviceSettings.Controls.Add(settingsPanel);
+ }
+
+ private IOutputDevicePlugin SelectedOutputDevicePlugin
+ {
+ get { return (IOutputDevicePlugin)comboBoxOutputDevice.SelectedItem; }
+ }
+
+ private void OnButtonPlayClick(object sender, EventArgs e)
+ {
+ if (!SelectedOutputDevicePlugin.IsAvailable)
+ {
+ MessageBox.Show("The selected output driver is not available on this system");
+ return;
+ }
+
+ if (waveOut != null)
+ {
+ if (waveOut.PlaybackState == PlaybackState.Playing)
+ {
+ return;
+ }
+ else if (waveOut.PlaybackState == PlaybackState.Paused)
+ {
+ waveOut.Play();
+ groupBoxDriverModel.Enabled = false;
+ return;
+ }
+ }
+
+ // we are in a stopped state
+ // TODO: only re-initialise if necessary
+
+ if (String.IsNullOrEmpty(fileName))
+ {
+ OnOpenFileClick(sender, e);
+ }
+ if (String.IsNullOrEmpty(fileName))
+ {
+ return;
+ }
+
+ try
+ {
+ CreateWaveOut();
+ }
+ catch (Exception driverCreateException)
+ {
+ MessageBox.Show(String.Format("{0}", driverCreateException.Message));
+ return;
+ }
+
+ ISampleProvider sampleProvider = null;
+ try
+ {
+ sampleProvider = CreateInputStream(fileName);
+ }
+ catch (Exception createException)
+ {
+ MessageBox.Show(String.Format("{0}", createException.Message), "Error Loading File");
+ return;
+ }
+
+
+ labelTotalTime.Text = String.Format("{0:00}:{1:00}", (int)audioFileReader.TotalTime.TotalMinutes,
+ audioFileReader.TotalTime.Seconds);
+
+ try
+ {
+ waveOut.Init(sampleProvider);
+ }
+ catch (Exception initException)
+ {
+ MessageBox.Show(String.Format("{0}", initException.Message), "Error Initializing Output");
+ return;
+ }
+
+ setVolumeDelegate(volumeSlider1.Volume);
+ groupBoxDriverModel.Enabled = false;
+ waveOut.Play();
+ }
+
+ private ISampleProvider CreateInputStream(string fileName)
+ {
+ this.audioFileReader = new AudioFileReader(fileName);
+
+ var sampleChannel = new SampleChannel(audioFileReader, true);
+ sampleChannel.PreVolumeMeter+= OnPreVolumeMeter;
+ this.setVolumeDelegate = (vol) => sampleChannel.Volume = vol;
+ var postVolumeMeter = new MeteringSampleProvider(sampleChannel);
+ postVolumeMeter.StreamVolume += OnPostVolumeMeter;
+
+ return postVolumeMeter;
+ }
+
+ void OnPreVolumeMeter(object sender, StreamVolumeEventArgs e)
+ {
+ // we know it is stereo
+ waveformPainter1.AddMax(e.MaxSampleValues[0]);
+ waveformPainter2.AddMax(e.MaxSampleValues[1]);
+ }
+
+ void OnPostVolumeMeter(object sender, StreamVolumeEventArgs e)
+ {
+ // we know it is stereo
+ volumeMeter1.Amplitude = e.MaxSampleValues[0];
+ volumeMeter2.Amplitude = e.MaxSampleValues[1];
+ }
+
+ private void CreateWaveOut()
+ {
+ CloseWaveOut();
+ var latency = (int)comboBoxLatency.SelectedItem;
+ waveOut = SelectedOutputDevicePlugin.CreateDevice(latency);
+ waveOut.PlaybackStopped += OnPlaybackStopped;
+ }
+
+ void OnPlaybackStopped(object sender, StoppedEventArgs e)
+ {
+ groupBoxDriverModel.Enabled = true;
+ if (e.Exception != null)
+ {
+ MessageBox.Show(e.Exception.Message, "Playback Device Error");
+ }
+ if (audioFileReader != null)
+ {
+ audioFileReader.Position = 0;
+ }
+ }
+
+ private void CloseWaveOut()
+ {
+ if (waveOut != null)
+ {
+ waveOut.Stop();
+ }
+ if (audioFileReader != null)
+ {
+ // this one really closes the file and ACM conversion
+ audioFileReader.Dispose();
+ setVolumeDelegate = null;
+ audioFileReader = null;
+ }
+ if (waveOut != null)
+ {
+ waveOut.Dispose();
+ waveOut = null;
+ }
+ }
+
+ private void Form1_Load(object sender, EventArgs e)
+ {
+ comboBoxLatency.Items.Add(25);
+ comboBoxLatency.Items.Add(50);
+ comboBoxLatency.Items.Add(100);
+ comboBoxLatency.Items.Add(150);
+ comboBoxLatency.Items.Add(200);
+ comboBoxLatency.Items.Add(300);
+ comboBoxLatency.Items.Add(400);
+ comboBoxLatency.Items.Add(500);
+ comboBoxLatency.SelectedIndex = 5;
+ }
+
+ private void OnButtonPauseClick(object sender, EventArgs e)
+ {
+ if (waveOut != null)
+ {
+ if (waveOut.PlaybackState == PlaybackState.Playing)
+ {
+ waveOut.Pause();
+ }
+ }
+ }
+
+ private void OnVolumeSliderChanged(object sender, EventArgs e)
+ {
+ if (setVolumeDelegate != null)
+ {
+ setVolumeDelegate(volumeSlider1.Volume);
+ }
+ }
+
+ private void OnButtonStopClick(object sender, EventArgs e)
+ {
+ if (waveOut != null)
+ {
+ waveOut.Stop();
+ }
+ }
+
+ private void OnTimerTick(object sender, EventArgs e)
+ {
+ if (waveOut != null && audioFileReader != null)
+ {
+ TimeSpan currentTime = (waveOut.PlaybackState == PlaybackState.Stopped) ? TimeSpan.Zero : audioFileReader.CurrentTime;
+ trackBarPosition.Value = Math.Min(trackBarPosition.Maximum, (int)(100 * currentTime.TotalSeconds / audioFileReader.TotalTime.TotalSeconds));
+ labelCurrentTime.Text = String.Format("{0:00}:{1:00}", (int)currentTime.TotalMinutes,
+ currentTime.Seconds);
+ }
+ else
+ {
+ trackBarPosition.Value = 0;
+ }
+ }
+
+ private void trackBarPosition_Scroll(object sender, EventArgs e)
+ {
+ if (waveOut != null)
+ {
+ audioFileReader.CurrentTime = TimeSpan.FromSeconds(audioFileReader.TotalTime.TotalSeconds * trackBarPosition.Value / 100.0);
+ }
+ }
+
+ private void OnOpenFileClick(object sender, EventArgs e)
+ {
+ var openFileDialog = new OpenFileDialog();
+ string allExtensions = "*.wav;*.aiff;*.mp3;*.aac";
+ openFileDialog.Filter = String.Format("All Supported Files|{0}|All Files (*.*)|*.*", allExtensions);
+ openFileDialog.FilterIndex = 1;
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ fileName = openFileDialog.FileName;
+ }
+ }
+ }
+}
+
diff --git a/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.resx b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.resx
new file mode 100644
index 00000000..efcc42f7
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanel.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 122, 17
+
+
\ No newline at end of file
diff --git a/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanelPlugin.cs b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanelPlugin.cs
new file mode 100644
index 00000000..03cc28c7
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/AudioPlaybackPanelPlugin.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel.Composition;
+using System.Windows.Forms;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ [Export(typeof(INAudioDemoPlugin))]
+ public class AudioPlaybackPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "Audio File Playback"; }
+ }
+
+ // using ExportFactory rather than Lazy allowing us to create
+ // a new one each time
+ // had to download a special MEF extension to allow this in .NET 3.5
+ [Import]
+ public ExportFactory PanelFactory { get; set; }
+
+ public Control CreatePanel()
+ {
+ return PanelFactory.CreateExport().Value; //new AudioPlaybackPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/DirectSoundOutPlugin.cs b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutPlugin.cs
new file mode 100644
index 00000000..1129f78e
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutPlugin.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+using System.Windows.Forms;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ [Export(typeof(IOutputDevicePlugin))]
+ class DirectSoundOutPlugin : IOutputDevicePlugin
+ {
+ private DirectSoundOutSettingsPanel settingsPanel;
+ private bool isAvailable;
+
+ public DirectSoundOutPlugin()
+ {
+ this.isAvailable = DirectSoundOut.Devices.Count() > 0;
+ }
+
+ public IWavePlayer CreateDevice(int latency)
+ {
+ return new DirectSoundOut(settingsPanel.SelectedDevice, latency);
+ }
+
+ public UserControl CreateSettingsPanel()
+ {
+ this.settingsPanel = new DirectSoundOutSettingsPanel();
+ return this.settingsPanel;
+ }
+
+ public string Name
+ {
+ get { return "DirectSound"; }
+ }
+
+ public bool IsAvailable
+ {
+ get { return isAvailable; }
+ }
+
+ public int Priority
+ {
+ get { return 2; }
+ }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.Designer.cs b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.Designer.cs
new file mode 100644
index 00000000..daae4c30
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.Designer.cs
@@ -0,0 +1,58 @@
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ partial class DirectSoundOutSettingsPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.comboBoxDirectSound = new System.Windows.Forms.ComboBox();
+ this.SuspendLayout();
+ //
+ // comboBoxDirectSound
+ //
+ this.comboBoxDirectSound.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxDirectSound.FormattingEnabled = true;
+ this.comboBoxDirectSound.Location = new System.Drawing.Point(3, 3);
+ this.comboBoxDirectSound.Name = "comboBoxDirectSound";
+ this.comboBoxDirectSound.Size = new System.Drawing.Size(232, 21);
+ this.comboBoxDirectSound.TabIndex = 18;
+ //
+ // DirectSoundOutSettingsPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.comboBoxDirectSound);
+ this.Name = "DirectSoundOutSettingsPanel";
+ this.Size = new System.Drawing.Size(243, 38);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ComboBox comboBoxDirectSound;
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.cs b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.cs
new file mode 100644
index 00000000..b7265e45
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ public partial class DirectSoundOutSettingsPanel : UserControl
+ {
+ public DirectSoundOutSettingsPanel()
+ {
+ InitializeComponent();
+ InitialiseDirectSoundControls();
+ }
+
+ private void InitialiseDirectSoundControls()
+ {
+ comboBoxDirectSound.DisplayMember = "Description";
+ comboBoxDirectSound.ValueMember = "Guid";
+ comboBoxDirectSound.DataSource = DirectSoundOut.Devices;
+ }
+
+ public Guid SelectedDevice
+ {
+ get { return (Guid)comboBoxDirectSound.SelectedValue; }
+ }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.resx b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.resx
new file mode 100644
index 00000000..5ea0895e
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/DirectSoundOutSettingsPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/AudioPlaybackDemo/IOutputDevicePlugin.cs b/NAudioDemo/AudioPlaybackDemo/IOutputDevicePlugin.cs
new file mode 100644
index 00000000..b2464228
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/IOutputDevicePlugin.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+using System.Windows.Forms;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ public interface IOutputDevicePlugin
+ {
+ IWavePlayer CreateDevice(int latency);
+ UserControl CreateSettingsPanel();
+ string Name { get; }
+ bool IsAvailable { get; }
+ int Priority { get; }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/WasapiOutPlugin.cs b/NAudioDemo/AudioPlaybackDemo/WasapiOutPlugin.cs
new file mode 100644
index 00000000..d9d4028e
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WasapiOutPlugin.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+using System.Windows.Forms;
+using NAudio.CoreAudioApi;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ [Export(typeof(IOutputDevicePlugin))]
+ class WasapiOutPlugin : IOutputDevicePlugin
+ {
+ WasapiOutSettingsPanel settingsPanel;
+
+ public IWavePlayer CreateDevice(int latency)
+ {
+ var wasapi = new WasapiOut(
+ settingsPanel.SelectedDevice,
+ settingsPanel.ShareMode,
+ settingsPanel.UseEventCallback,
+ latency);
+ return wasapi;
+ }
+
+ public UserControl CreateSettingsPanel()
+ {
+ this.settingsPanel = new WasapiOutSettingsPanel();
+ return settingsPanel;
+ }
+
+ public string Name
+ {
+ get { return "WasapiOut"; }
+ }
+
+ public bool IsAvailable
+ {
+ // supported on Vista and above
+ get { return Environment.OSVersion.Version.Major >= 6; }
+ }
+
+ public int Priority
+ {
+ get { return 3; }
+ }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.Designer.cs b/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.Designer.cs
new file mode 100644
index 00000000..2411df6a
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.Designer.cs
@@ -0,0 +1,85 @@
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ partial class WasapiOutSettingsPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.comboBoxWaspai = new System.Windows.Forms.ComboBox();
+ this.checkBoxWasapiEventCallback = new System.Windows.Forms.CheckBox();
+ this.checkBoxWasapiExclusiveMode = new System.Windows.Forms.CheckBox();
+ this.SuspendLayout();
+ //
+ // comboBoxWaspai
+ //
+ this.comboBoxWaspai.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxWaspai.FormattingEnabled = true;
+ this.comboBoxWaspai.Location = new System.Drawing.Point(3, 3);
+ this.comboBoxWaspai.Name = "comboBoxWaspai";
+ this.comboBoxWaspai.Size = new System.Drawing.Size(232, 21);
+ this.comboBoxWaspai.TabIndex = 20;
+ //
+ // checkBoxWasapiEventCallback
+ //
+ this.checkBoxWasapiEventCallback.AutoSize = true;
+ this.checkBoxWasapiEventCallback.Location = new System.Drawing.Point(3, 30);
+ this.checkBoxWasapiEventCallback.Name = "checkBoxWasapiEventCallback";
+ this.checkBoxWasapiEventCallback.Size = new System.Drawing.Size(98, 17);
+ this.checkBoxWasapiEventCallback.TabIndex = 19;
+ this.checkBoxWasapiEventCallback.Text = "Event Callback";
+ this.checkBoxWasapiEventCallback.UseVisualStyleBackColor = true;
+ //
+ // checkBoxWasapiExclusiveMode
+ //
+ this.checkBoxWasapiExclusiveMode.AutoSize = true;
+ this.checkBoxWasapiExclusiveMode.Location = new System.Drawing.Point(134, 30);
+ this.checkBoxWasapiExclusiveMode.Name = "checkBoxWasapiExclusiveMode";
+ this.checkBoxWasapiExclusiveMode.Size = new System.Drawing.Size(101, 17);
+ this.checkBoxWasapiExclusiveMode.TabIndex = 18;
+ this.checkBoxWasapiExclusiveMode.Text = "Exclusive Mode";
+ this.checkBoxWasapiExclusiveMode.UseVisualStyleBackColor = true;
+ //
+ // WasapiOutSettingsPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.comboBoxWaspai);
+ this.Controls.Add(this.checkBoxWasapiEventCallback);
+ this.Controls.Add(this.checkBoxWasapiExclusiveMode);
+ this.Name = "WasapiOutSettingsPanel";
+ this.Size = new System.Drawing.Size(245, 57);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ComboBox comboBoxWaspai;
+ private System.Windows.Forms.CheckBox checkBoxWasapiEventCallback;
+ private System.Windows.Forms.CheckBox checkBoxWasapiExclusiveMode;
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.cs b/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.cs
new file mode 100644
index 00000000..f12e7a70
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.CoreAudioApi;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ public partial class WasapiOutSettingsPanel : UserControl
+ {
+ public WasapiOutSettingsPanel()
+ {
+ InitializeComponent();
+ InitialiseWasapiControls();
+ }
+
+ class WasapiDeviceComboItem
+ {
+ public string Description { get; set; }
+ public MMDevice Device { get; set; }
+ }
+
+ private void InitialiseWasapiControls()
+ {
+ var enumerator = new MMDeviceEnumerator();
+ var endPoints = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active);
+ var comboItems = new List();
+ foreach (var endPoint in endPoints)
+ {
+ var comboItem = new WasapiDeviceComboItem();
+ comboItem.Description = string.Format("{0} ({1})", endPoint.FriendlyName, endPoint.DeviceFriendlyName);
+ comboItem.Device = endPoint;
+ comboItems.Add(comboItem);
+ }
+ comboBoxWaspai.DisplayMember = "Description";
+ comboBoxWaspai.ValueMember = "Device";
+ comboBoxWaspai.DataSource = comboItems;
+ }
+
+ public MMDevice SelectedDevice { get { return (MMDevice)comboBoxWaspai.SelectedValue; } }
+
+ public AudioClientShareMode ShareMode
+ {
+ get
+ {
+ return checkBoxWasapiExclusiveMode.Checked ?
+ AudioClientShareMode.Exclusive :
+ AudioClientShareMode.Shared;
+ }
+ }
+
+ public bool UseEventCallback { get { return checkBoxWasapiEventCallback.Checked; } }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.resx b/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.resx
new file mode 100644
index 00000000..5ea0895e
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WasapiOutSettingsPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/AudioPlaybackDemo/WaveOutPlugin.cs b/NAudioDemo/AudioPlaybackDemo/WaveOutPlugin.cs
new file mode 100644
index 00000000..12b48dc1
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WaveOutPlugin.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+using System.Windows.Forms;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ [Export(typeof(IOutputDevicePlugin))]
+ class WaveOutPlugin : IOutputDevicePlugin
+ {
+ private WaveOutSettingsPanel waveOutSettingsPanel;
+
+ public IWavePlayer CreateDevice(int latency)
+ {
+ IWavePlayer device;
+ WaveCallbackStrategy strategy = waveOutSettingsPanel.CallbackStrategy;
+ if (strategy == WaveCallbackStrategy.Event)
+ {
+ var waveOut = new WaveOutEvent();
+ waveOut.DeviceNumber = waveOutSettingsPanel.SelectedDeviceNumber;
+ waveOut.DesiredLatency = latency;
+ device = waveOut;
+ }
+ else
+ {
+ WaveCallbackInfo callbackInfo = strategy == WaveCallbackStrategy.NewWindow ? WaveCallbackInfo.NewWindow() : WaveCallbackInfo.FunctionCallback();
+ WaveOut outputDevice = new WaveOut(callbackInfo);
+ outputDevice.DeviceNumber = waveOutSettingsPanel.SelectedDeviceNumber;
+ outputDevice.DesiredLatency = latency;
+ device = outputDevice;
+ }
+ // TODO: configurable number of buffers
+
+ return device;
+ }
+
+ public UserControl CreateSettingsPanel()
+ {
+ this.waveOutSettingsPanel = new WaveOutSettingsPanel();
+ return waveOutSettingsPanel;
+ }
+
+ public string Name
+ {
+ get { return "WaveOut"; }
+ }
+
+ public bool IsAvailable
+ {
+ get { return WaveOut.DeviceCount > 0; }
+ }
+
+ public int Priority
+ {
+ get { return 1; }
+ }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.Designer.cs b/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.Designer.cs
new file mode 100644
index 00000000..d6f226eb
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.Designer.cs
@@ -0,0 +1,83 @@
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ partial class WaveOutSettingsPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.comboBoxWaveOutDevice = new System.Windows.Forms.ComboBox();
+ this.comboBoxCallback = new System.Windows.Forms.ComboBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // comboBoxWaveOutDevice
+ //
+ this.comboBoxWaveOutDevice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxWaveOutDevice.FormattingEnabled = true;
+ this.comboBoxWaveOutDevice.Location = new System.Drawing.Point(7, 34);
+ this.comboBoxWaveOutDevice.Name = "comboBoxWaveOutDevice";
+ this.comboBoxWaveOutDevice.Size = new System.Drawing.Size(232, 21);
+ this.comboBoxWaveOutDevice.TabIndex = 19;
+ //
+ // comboBoxCallback
+ //
+ this.comboBoxCallback.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxCallback.FormattingEnabled = true;
+ this.comboBoxCallback.Location = new System.Drawing.Point(118, 7);
+ this.comboBoxCallback.Name = "comboBoxCallback";
+ this.comboBoxCallback.Size = new System.Drawing.Size(121, 21);
+ this.comboBoxCallback.TabIndex = 20;
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(4, 10);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(108, 13);
+ this.label1.TabIndex = 21;
+ this.label1.Text = "Callback Mechanism:";
+ //
+ // WaveOutSettingsPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.comboBoxCallback);
+ this.Controls.Add(this.comboBoxWaveOutDevice);
+ this.Name = "WaveOutSettingsPanel";
+ this.Size = new System.Drawing.Size(253, 76);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ComboBox comboBoxWaveOutDevice;
+ private System.Windows.Forms.ComboBox comboBoxCallback;
+ private System.Windows.Forms.Label label1;
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.cs b/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.cs
new file mode 100644
index 00000000..5bbdca7d
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+
+namespace NAudioDemo.AudioPlaybackDemo
+{
+ public partial class WaveOutSettingsPanel : UserControl
+ {
+
+
+ public WaveOutSettingsPanel()
+ {
+ InitializeComponent();
+ InitialiseDeviceCombo();
+ InitialiseStrategyCombo();
+ }
+
+ class CallbackComboItem
+ {
+ public CallbackComboItem(string text, WaveCallbackStrategy strategy)
+ {
+ this.Text = text;
+ this.Strategy = strategy;
+ }
+ public string Text { get; private set; }
+ public WaveCallbackStrategy Strategy { get; private set; }
+ }
+
+ private void InitialiseDeviceCombo()
+ {
+ for (int deviceId = 0; deviceId < WaveOut.DeviceCount; deviceId++)
+ {
+ var capabilities = WaveOut.GetCapabilities(deviceId);
+ comboBoxWaveOutDevice.Items.Add(String.Format("Device {0} ({1})", deviceId, capabilities.ProductName));
+ }
+ if (comboBoxWaveOutDevice.Items.Count > 0)
+ {
+ comboBoxWaveOutDevice.SelectedIndex = 0;
+ }
+ }
+
+ private void InitialiseStrategyCombo()
+ {
+ comboBoxCallback.DisplayMember = "Text";
+ comboBoxCallback.ValueMember = "Strategy";
+ comboBoxCallback.Items.Add(new CallbackComboItem("Window", WaveCallbackStrategy.NewWindow));
+ comboBoxCallback.Items.Add(new CallbackComboItem("Function", WaveCallbackStrategy.FunctionCallback));
+ comboBoxCallback.Items.Add(new CallbackComboItem("Event", WaveCallbackStrategy.Event));
+ comboBoxCallback.SelectedIndex = 0;
+ }
+
+ public int SelectedDeviceNumber { get { return comboBoxWaveOutDevice.SelectedIndex; } }
+
+ public WaveCallbackStrategy CallbackStrategy { get { return ((CallbackComboItem)comboBoxCallback.SelectedItem).Strategy; } }
+ }
+}
diff --git a/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.resx b/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.resx
new file mode 100644
index 00000000..5ea0895e
--- /dev/null
+++ b/NAudioDemo/AudioPlaybackDemo/WaveOutSettingsPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/FadeInOutDemo/FadeInOutPanel.Designer.cs b/NAudioDemo/FadeInOutDemo/FadeInOutPanel.Designer.cs
new file mode 100644
index 00000000..999362fc
--- /dev/null
+++ b/NAudioDemo/FadeInOutDemo/FadeInOutPanel.Designer.cs
@@ -0,0 +1,185 @@
+namespace NAudioDemo.FadeInOutDemo
+{
+ partial class FadeInOutPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.buttonPlay = new System.Windows.Forms.Button();
+ this.buttonStop = new System.Windows.Forms.Button();
+ this.volumeSlider1 = new NAudio.Gui.VolumeSlider();
+ this.buttonOpen = new System.Windows.Forms.Button();
+ this.labelNowTime = new System.Windows.Forms.Label();
+ this.labelTotalTime = new System.Windows.Forms.Label();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.buttonBeginFadeIn = new System.Windows.Forms.Button();
+ this.buttonBeginFadeOut = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.textBoxFadeDuration = new System.Windows.Forms.TextBox();
+ this.SuspendLayout();
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.Location = new System.Drawing.Point(91, 31);
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(75, 23);
+ this.buttonPlay.TabIndex = 1;
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.UseVisualStyleBackColor = true;
+ this.buttonPlay.Click += new System.EventHandler(this.buttonPlay_Click);
+ //
+ // buttonStop
+ //
+ this.buttonStop.Location = new System.Drawing.Point(173, 30);
+ this.buttonStop.Name = "buttonStop";
+ this.buttonStop.Size = new System.Drawing.Size(75, 23);
+ this.buttonStop.TabIndex = 2;
+ this.buttonStop.Text = "Stop";
+ this.buttonStop.UseVisualStyleBackColor = true;
+ this.buttonStop.Click += new System.EventHandler(this.buttonStop_Click);
+ //
+ // volumeSlider1
+ //
+ this.volumeSlider1.Location = new System.Drawing.Point(307, 37);
+ this.volumeSlider1.Name = "volumeSlider1";
+ this.volumeSlider1.Size = new System.Drawing.Size(96, 16);
+ this.volumeSlider1.TabIndex = 5;
+ this.volumeSlider1.VolumeChanged += new System.EventHandler(this.volumeSlider1_VolumeChanged);
+ //
+ // buttonOpen
+ //
+ this.buttonOpen.Location = new System.Drawing.Point(10, 31);
+ this.buttonOpen.Name = "buttonOpen";
+ this.buttonOpen.Size = new System.Drawing.Size(75, 23);
+ this.buttonOpen.TabIndex = 1;
+ this.buttonOpen.Text = "Open";
+ this.buttonOpen.UseVisualStyleBackColor = true;
+ this.buttonOpen.Click += new System.EventHandler(this.buttonOpen_Click);
+ //
+ // labelNowTime
+ //
+ this.labelNowTime.AutoSize = true;
+ this.labelNowTime.Location = new System.Drawing.Point(436, 40);
+ this.labelNowTime.Name = "labelNowTime";
+ this.labelNowTime.Size = new System.Drawing.Size(34, 13);
+ this.labelNowTime.TabIndex = 6;
+ this.labelNowTime.Text = "00:00";
+ //
+ // labelTotalTime
+ //
+ this.labelTotalTime.AutoSize = true;
+ this.labelTotalTime.Location = new System.Drawing.Point(476, 40);
+ this.labelTotalTime.Name = "labelTotalTime";
+ this.labelTotalTime.Size = new System.Drawing.Size(34, 13);
+ this.labelTotalTime.TabIndex = 6;
+ this.labelTotalTime.Text = "00:00";
+ //
+ // buttonBeginFadeIn
+ //
+ this.buttonBeginFadeIn.Location = new System.Drawing.Point(10, 79);
+ this.buttonBeginFadeIn.Name = "buttonBeginFadeIn";
+ this.buttonBeginFadeIn.Size = new System.Drawing.Size(109, 23);
+ this.buttonBeginFadeIn.TabIndex = 7;
+ this.buttonBeginFadeIn.Text = "Begin Fade In";
+ this.buttonBeginFadeIn.UseVisualStyleBackColor = true;
+ this.buttonBeginFadeIn.Click += new System.EventHandler(this.buttonBeginFadeIn_Click);
+ //
+ // buttonBeginFadeOut
+ //
+ this.buttonBeginFadeOut.Location = new System.Drawing.Point(10, 108);
+ this.buttonBeginFadeOut.Name = "buttonBeginFadeOut";
+ this.buttonBeginFadeOut.Size = new System.Drawing.Size(109, 23);
+ this.buttonBeginFadeOut.TabIndex = 7;
+ this.buttonBeginFadeOut.Text = "Begin Fade Out";
+ this.buttonBeginFadeOut.UseVisualStyleBackColor = true;
+ this.buttonBeginFadeOut.Click += new System.EventHandler(this.buttonBeginFadeOut_Click);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(10, 4);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(380, 13);
+ this.label1.TabIndex = 8;
+ this.label1.Text = "Play a file and use the Begin Fade In or Begin Fade Out buttons to trigger fades";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(148, 84);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(145, 13);
+ this.label2.TabIndex = 9;
+ this.label2.Text = "Fade duration in milliseconds:";
+ //
+ // textBoxFadeDuration
+ //
+ this.textBoxFadeDuration.Location = new System.Drawing.Point(307, 81);
+ this.textBoxFadeDuration.Name = "textBoxFadeDuration";
+ this.textBoxFadeDuration.Size = new System.Drawing.Size(100, 20);
+ this.textBoxFadeDuration.TabIndex = 10;
+ this.textBoxFadeDuration.Text = "5000";
+ //
+ // FadeInOutPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.textBoxFadeDuration);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.buttonBeginFadeOut);
+ this.Controls.Add(this.buttonBeginFadeIn);
+ this.Controls.Add(this.labelTotalTime);
+ this.Controls.Add(this.labelNowTime);
+ this.Controls.Add(this.volumeSlider1);
+ this.Controls.Add(this.buttonStop);
+ this.Controls.Add(this.buttonOpen);
+ this.Controls.Add(this.buttonPlay);
+ this.Name = "FadeInOutPanel";
+ this.Size = new System.Drawing.Size(821, 268);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button buttonPlay;
+ private System.Windows.Forms.Button buttonStop;
+ private NAudio.Gui.VolumeSlider volumeSlider1;
+ private System.Windows.Forms.Button buttonOpen;
+ private System.Windows.Forms.Label labelNowTime;
+ private System.Windows.Forms.Label labelTotalTime;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.Button buttonBeginFadeIn;
+ private System.Windows.Forms.Button buttonBeginFadeOut;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.TextBox textBoxFadeDuration;
+ }
+}
diff --git a/NAudioDemo/FadeInOutDemo/FadeInOutPanel.cs b/NAudioDemo/FadeInOutDemo/FadeInOutPanel.cs
new file mode 100644
index 00000000..b42fb2f2
--- /dev/null
+++ b/NAudioDemo/FadeInOutDemo/FadeInOutPanel.cs
@@ -0,0 +1,164 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Wave;
+using System.Diagnostics;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudioDemo.FadeInOutDemo
+{
+ public partial class FadeInOutPanel : UserControl
+ {
+ private IWavePlayer wavePlayer;
+ private AudioFileReader file;
+ private FadeInOutSampleProvider fadeInOut;
+ private string fileName;
+
+ public FadeInOutPanel()
+ {
+ InitializeComponent();
+ EnableButtons(false);
+ this.Disposed += new EventHandler(SimplePlaybackPanel_Disposed);
+ this.timer1.Interval = 250;
+ this.timer1.Tick += new EventHandler(timer1_Tick);
+ }
+
+ private static string FormatTimeSpan(TimeSpan ts)
+ {
+ return string.Format("{0:D2}:{1:D2}", (int)ts.TotalMinutes, ts.Seconds);
+ }
+
+ void timer1_Tick(object sender, EventArgs e)
+ {
+ if (file != null)
+ {
+ labelNowTime.Text = FormatTimeSpan(file.CurrentTime);
+ labelTotalTime.Text = FormatTimeSpan(file.TotalTime);
+ }
+ }
+
+ void SimplePlaybackPanel_Disposed(object sender, EventArgs e)
+ {
+ CleanUp();
+ }
+
+ private void buttonPlay_Click(object sender, EventArgs e)
+ {
+ if (SelectInputFile())
+ {
+ BeginPlayback(fileName);
+ }
+ }
+
+ private bool SelectInputFile()
+ {
+ if (fileName == null)
+ {
+ OpenFileDialog ofd = new OpenFileDialog();
+ ofd.Filter = "Audio Files|*.mp3;*.wav;*.aiff";
+
+ if (ofd.ShowDialog() == DialogResult.OK)
+ {
+ this.fileName = ofd.FileName;
+ }
+ }
+ return fileName != null;
+ }
+
+ private void BeginPlayback(string filename)
+ {
+ Debug.Assert(this.wavePlayer == null);
+ this.wavePlayer = new WaveOutEvent();
+ this.file = new AudioFileReader(filename);
+ this.fadeInOut = new FadeInOutSampleProvider(file);
+ this.file.Volume = volumeSlider1.Volume;
+ this.wavePlayer.Init(fadeInOut);
+ this.wavePlayer.PlaybackStopped += wavePlayer_PlaybackStopped;
+ this.wavePlayer.Play();
+ EnableButtons(true);
+ timer1.Enabled = true; // timer for updating current time label
+ }
+
+ private void EnableButtons(bool playing)
+ {
+ buttonPlay.Enabled = !playing;
+ buttonStop.Enabled = playing;
+ buttonOpen.Enabled = !playing;
+ buttonBeginFadeIn.Enabled = playing;
+ buttonBeginFadeOut.Enabled = playing;
+ }
+
+ void wavePlayer_PlaybackStopped(object sender, StoppedEventArgs e)
+ {
+ // we want to be always on the GUI thread and be able to change GUI components
+ Debug.Assert(!this.InvokeRequired, "PlaybackStopped on wrong thread");
+ // we want it to be safe to clean up input stream and playback device in the handler for PlaybackStopped
+ CleanUp();
+ EnableButtons(false);
+ timer1.Enabled = false;
+ labelNowTime.Text = "00:00";
+ }
+
+ private void CleanUp()
+ {
+ if (this.file != null)
+ {
+ this.file.Dispose();
+ this.file = null;
+ }
+ if (this.wavePlayer != null)
+ {
+ this.wavePlayer.Dispose();
+ this.wavePlayer = null;
+ }
+ this.fadeInOut = null;
+ }
+
+ private void buttonStop_Click(object sender, EventArgs e)
+ {
+ this.wavePlayer.Stop();
+ // don't set button states now, we'll wait for our PlaybackStopped to come
+ }
+
+ private void volumeSlider1_VolumeChanged(object sender, EventArgs e)
+ {
+ if (this.file != null)
+ {
+ this.file.Volume = volumeSlider1.Volume;
+ }
+ }
+
+ private void buttonOpen_Click(object sender, EventArgs e)
+ {
+ SelectInputFile();
+ }
+
+ private int GetFadeDuration()
+ {
+ int fadeDuration = 5000;
+ int.TryParse(textBoxFadeDuration.Text, out fadeDuration);
+ return fadeDuration;
+ }
+
+ private void buttonBeginFadeIn_Click(object sender, EventArgs e)
+ {
+ if (this.fadeInOut != null)
+ {
+ this.fadeInOut.BeginFadeIn(GetFadeDuration());
+ }
+ }
+
+ private void buttonBeginFadeOut_Click(object sender, EventArgs e)
+ {
+ if (this.fadeInOut != null)
+ {
+ this.fadeInOut.BeginFadeOut(GetFadeDuration());
+ }
+ }
+ }
+}
diff --git a/NAudioDemo/FadeInOutDemo/FadeInOutPanel.resx b/NAudioDemo/FadeInOutDemo/FadeInOutPanel.resx
new file mode 100644
index 00000000..6999fbfa
--- /dev/null
+++ b/NAudioDemo/FadeInOutDemo/FadeInOutPanel.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/NAudioDemo/FadeInOutDemo/FadeInOutPlugin.cs b/NAudioDemo/FadeInOutDemo/FadeInOutPlugin.cs
new file mode 100644
index 00000000..29c3f85e
--- /dev/null
+++ b/NAudioDemo/FadeInOutDemo/FadeInOutPlugin.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.FadeInOutDemo
+{
+ [Export(typeof(INAudioDemoPlugin))]
+ class FadeInOutPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "Fade In Out"; }
+ }
+
+ public System.Windows.Forms.Control CreatePanel()
+ {
+ return new FadeInOutPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/INAudioDemoPlugin.cs b/NAudioDemo/INAudioDemoPlugin.cs
new file mode 100644
index 00000000..ada50917
--- /dev/null
+++ b/NAudioDemo/INAudioDemoPlugin.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Forms;
+
+namespace NAudioDemo
+{
+ public interface INAudioDemoPlugin
+ {
+ string Name { get; }
+ Control CreatePanel();
+ }
+}
diff --git a/NAudioDemo/Images.Designer.cs b/NAudioDemo/Images.Designer.cs
new file mode 100644
index 00000000..babef87b
--- /dev/null
+++ b/NAudioDemo/Images.Designer.cs
@@ -0,0 +1,126 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.225
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace NAudioDemo {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Images {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Images() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NAudioDemo.Images", typeof(Images).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ internal static System.Drawing.Bitmap Back {
+ get {
+ object obj = ResourceManager.GetObject("Back", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Forward {
+ get {
+ object obj = ResourceManager.GetObject("Forward", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Loop {
+ get {
+ object obj = ResourceManager.GetObject("Loop", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Open {
+ get {
+ object obj = ResourceManager.GetObject("Open", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Pause {
+ get {
+ object obj = ResourceManager.GetObject("Pause", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Play {
+ get {
+ object obj = ResourceManager.GetObject("Play", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Rewind {
+ get {
+ object obj = ResourceManager.GetObject("Rewind", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Shuffle {
+ get {
+ object obj = ResourceManager.GetObject("Shuffle", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ internal static System.Drawing.Bitmap Stop {
+ get {
+ object obj = ResourceManager.GetObject("Stop", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+ }
+}
diff --git a/NAudioDemo/Images.resx b/NAudioDemo/Images.resx
new file mode 100644
index 00000000..09bd3f9b
--- /dev/null
+++ b/NAudioDemo/Images.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ Resources\Back.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Forward.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Loop.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Open.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Pause.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Play.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Rewind.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Shuffle.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ Resources\Stop.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
\ No newline at end of file
diff --git a/NAudioDemo/LoopStream.cs b/NAudioDemo/LoopStream.cs
new file mode 100644
index 00000000..dd7ffa1c
--- /dev/null
+++ b/NAudioDemo/LoopStream.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudioDemo
+{
+ class LoopStream : WaveStream
+ {
+ WaveStream sourceStream;
+
+ public LoopStream(WaveStream source)
+ {
+ this.sourceStream = source;
+ }
+
+
+ public override WaveFormat WaveFormat
+ {
+ get { return sourceStream.WaveFormat; }
+ }
+
+ public override long Length
+ {
+ get { return long.MaxValue / 32; }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ return sourceStream.Position;
+ }
+ set
+ {
+ sourceStream.Position = value;
+ }
+ }
+
+ public override bool HasData(int count)
+ {
+ // infinite loop
+ return true;
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int read = 0;
+ while (read < count)
+ {
+ int required = count - read;
+ int readThisTime = sourceStream.Read(buffer, offset + read, required);
+ if (readThisTime < required)
+ {
+ sourceStream.Position = 0;
+ }
+
+ if (sourceStream.Position >= sourceStream.Length)
+ {
+ sourceStream.Position = 0;
+ }
+ read += readThisTime;
+ }
+ return read;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ sourceStream.Dispose();
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/NAudioDemo/MainForm.Designer.cs b/NAudioDemo/MainForm.Designer.cs
new file mode 100644
index 00000000..9b259daa
--- /dev/null
+++ b/NAudioDemo/MainForm.Designer.cs
@@ -0,0 +1,102 @@
+namespace NAudioDemo
+{
+ partial class MainForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.listBoxDemos = new System.Windows.Forms.ListBox();
+ this.buttonLoadDemo = new System.Windows.Forms.Button();
+ this.panelDemo = new System.Windows.Forms.Panel();
+ this.label1 = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // listBoxDemos
+ //
+ this.listBoxDemos.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.listBoxDemos.FormattingEnabled = true;
+ this.listBoxDemos.Location = new System.Drawing.Point(12, 51);
+ this.listBoxDemos.Name = "listBoxDemos";
+ this.listBoxDemos.Size = new System.Drawing.Size(120, 329);
+ this.listBoxDemos.TabIndex = 3;
+ this.listBoxDemos.DoubleClick += new System.EventHandler(this.listBoxDemos_DoubleClick);
+ //
+ // buttonLoadDemo
+ //
+ this.buttonLoadDemo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
+ this.buttonLoadDemo.Location = new System.Drawing.Point(13, 397);
+ this.buttonLoadDemo.Name = "buttonLoadDemo";
+ this.buttonLoadDemo.Size = new System.Drawing.Size(75, 23);
+ this.buttonLoadDemo.TabIndex = 4;
+ this.buttonLoadDemo.Text = "Load";
+ this.buttonLoadDemo.UseVisualStyleBackColor = true;
+ this.buttonLoadDemo.Click += new System.EventHandler(this.buttonLoadDemo_Click);
+ //
+ // panelDemo
+ //
+ this.panelDemo.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.panelDemo.Location = new System.Drawing.Point(139, 14);
+ this.panelDemo.Name = "panelDemo";
+ this.panelDemo.Size = new System.Drawing.Size(681, 405);
+ this.panelDemo.TabIndex = 5;
+ //
+ // label1
+ //
+ this.label1.Location = new System.Drawing.Point(13, 14);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(100, 35);
+ this.label1.TabIndex = 6;
+ this.label1.Text = "Select a demo, and click Load";
+ //
+ // MainForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(829, 432);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.panelDemo);
+ this.Controls.Add(this.buttonLoadDemo);
+ this.Controls.Add(this.listBoxDemos);
+ this.Name = "MainForm";
+ this.Text = "NAudio Demo";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ListBox listBoxDemos;
+ private System.Windows.Forms.Button buttonLoadDemo;
+ private System.Windows.Forms.Panel panelDemo;
+ private System.Windows.Forms.Label label1;
+
+ }
+}
+
diff --git a/NAudioDemo/MainForm.cs b/NAudioDemo/MainForm.cs
new file mode 100644
index 00000000..d54195c8
--- /dev/null
+++ b/NAudioDemo/MainForm.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo
+{
+ [Export]
+ public partial class MainForm : Form
+ {
+ [ImportingConstructor]
+ public MainForm([ImportMany] IEnumerable demos)
+ {
+ InitializeComponent();
+ listBoxDemos.DisplayMember = "Name";
+ foreach (var demo in demos)
+ {
+ listBoxDemos.Items.Add(demo);
+ }
+
+ this.Text = this.Text + ((System.Runtime.InteropServices.Marshal.SizeOf(IntPtr.Zero) == 8) ? " (x64)" : " (x86)");
+ }
+
+ private INAudioDemoPlugin currentPlugin;
+
+ private void buttonLoadDemo_Click(object sender, EventArgs e)
+ {
+ var plugin = (INAudioDemoPlugin)listBoxDemos.SelectedItem;
+ if (plugin != currentPlugin)
+ {
+ this.currentPlugin = plugin;
+ DisposeCurrentDemo();
+ var control = plugin.CreatePanel();
+ control.Dock = DockStyle.Fill;
+ panelDemo.Controls.Add(control);
+ }
+ }
+
+ private void DisposeCurrentDemo()
+ {
+ if (panelDemo.Controls.Count > 0)
+ {
+ panelDemo.Controls[0].Dispose();
+ panelDemo.Controls.Clear();
+ GC.Collect();
+ }
+ }
+
+ private void listBoxDemos_DoubleClick(object sender, EventArgs e)
+ {
+ buttonLoadDemo_Click(sender, e);
+ }
+
+ private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ DisposeCurrentDemo();
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/MainForm.resx b/NAudioDemo/MainForm.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/NAudioDemo/MainForm.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.Designer.cs b/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.Designer.cs
new file mode 100644
index 00000000..94dade56
--- /dev/null
+++ b/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.Designer.cs
@@ -0,0 +1,177 @@
+namespace NAudioDemo.MediaFoundationDemo
+{
+ partial class MediaFoundationDemoPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MediaFoundationDemoPanel));
+ this.buttonPlay = new System.Windows.Forms.Button();
+ this.buttonStop = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.buttonLoadFile = new System.Windows.Forms.Button();
+ this.trackBar1 = new System.Windows.Forms.TrackBar();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.labelPosition = new System.Windows.Forms.Label();
+ this.radioButtonWaveOut = new System.Windows.Forms.RadioButton();
+ this.radioButtonWasapi = new System.Windows.Forms.RadioButton();
+ this.label2 = new System.Windows.Forms.Label();
+ ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();
+ this.SuspendLayout();
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.Location = new System.Drawing.Point(84, 52);
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(75, 23);
+ this.buttonPlay.TabIndex = 0;
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.UseVisualStyleBackColor = true;
+ this.buttonPlay.Click += new System.EventHandler(this.OnButtonPlayClick);
+ //
+ // buttonStop
+ //
+ this.buttonStop.Location = new System.Drawing.Point(165, 52);
+ this.buttonStop.Name = "buttonStop";
+ this.buttonStop.Size = new System.Drawing.Size(75, 23);
+ this.buttonStop.TabIndex = 1;
+ this.buttonStop.Text = "Stop";
+ this.buttonStop.UseVisualStyleBackColor = true;
+ this.buttonStop.Click += new System.EventHandler(this.OnButtonStopClick);
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label1.Location = new System.Drawing.Point(4, 4);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(582, 45);
+ this.label1.TabIndex = 2;
+ this.label1.Text = "This demo uses Media Foundation to play audio. This will only work on Vista and a" +
+ "bove. It will be able to play formats like MP3, AAC, WMA, assuiming you have the" +
+ " appropriate decoders on your machine.";
+ //
+ // buttonLoadFile
+ //
+ this.buttonLoadFile.Location = new System.Drawing.Point(3, 52);
+ this.buttonLoadFile.Name = "buttonLoadFile";
+ this.buttonLoadFile.Size = new System.Drawing.Size(75, 23);
+ this.buttonLoadFile.TabIndex = 0;
+ this.buttonLoadFile.Text = "Load";
+ this.buttonLoadFile.UseVisualStyleBackColor = true;
+ this.buttonLoadFile.Click += new System.EventHandler(this.OnButtonLoadFileClick);
+ //
+ // trackBar1
+ //
+ this.trackBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.trackBar1.Location = new System.Drawing.Point(3, 91);
+ this.trackBar1.Maximum = 1000;
+ this.trackBar1.Name = "trackBar1";
+ this.trackBar1.Size = new System.Drawing.Size(569, 45);
+ this.trackBar1.TabIndex = 3;
+ this.trackBar1.TickFrequency = 10;
+ this.trackBar1.Scroll += new System.EventHandler(this.OnTrackBarScroll);
+ //
+ // timer1
+ //
+ this.timer1.Tick += new System.EventHandler(this.OnTimerTick);
+ //
+ // labelPosition
+ //
+ this.labelPosition.AutoSize = true;
+ this.labelPosition.Location = new System.Drawing.Point(255, 57);
+ this.labelPosition.Name = "labelPosition";
+ this.labelPosition.Size = new System.Drawing.Size(0, 13);
+ this.labelPosition.TabIndex = 4;
+ //
+ // radioButtonWaveOut
+ //
+ this.radioButtonWaveOut.AutoSize = true;
+ this.radioButtonWaveOut.Checked = true;
+ this.radioButtonWaveOut.Location = new System.Drawing.Point(16, 143);
+ this.radioButtonWaveOut.Name = "radioButtonWaveOut";
+ this.radioButtonWaveOut.Size = new System.Drawing.Size(71, 17);
+ this.radioButtonWaveOut.TabIndex = 5;
+ this.radioButtonWaveOut.TabStop = true;
+ this.radioButtonWaveOut.Text = "WaveOut";
+ this.radioButtonWaveOut.UseVisualStyleBackColor = true;
+ this.radioButtonWaveOut.CheckedChanged += new System.EventHandler(this.OnRadioButtonWaveOutCheckedChanged);
+ //
+ // radioButtonWasapi
+ //
+ this.radioButtonWasapi.AutoSize = true;
+ this.radioButtonWasapi.Location = new System.Drawing.Point(16, 166);
+ this.radioButtonWasapi.Name = "radioButtonWasapi";
+ this.radioButtonWasapi.Size = new System.Drawing.Size(67, 17);
+ this.radioButtonWasapi.TabIndex = 5;
+ this.radioButtonWasapi.Text = "WASAPI";
+ this.radioButtonWasapi.UseVisualStyleBackColor = true;
+ //
+ // label2
+ //
+ this.label2.Location = new System.Drawing.Point(93, 137);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(394, 56);
+ this.label2.TabIndex = 6;
+ this.label2.Text = resources.GetString("label2.Text");
+ //
+ // MediaFoundationDemoPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.radioButtonWasapi);
+ this.Controls.Add(this.radioButtonWaveOut);
+ this.Controls.Add(this.labelPosition);
+ this.Controls.Add(this.trackBar1);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.buttonStop);
+ this.Controls.Add(this.buttonLoadFile);
+ this.Controls.Add(this.buttonPlay);
+ this.Name = "MediaFoundationDemoPanel";
+ this.Size = new System.Drawing.Size(589, 394);
+ ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button buttonPlay;
+ private System.Windows.Forms.Button buttonStop;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button buttonLoadFile;
+ private System.Windows.Forms.TrackBar trackBar1;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.Label labelPosition;
+ private System.Windows.Forms.RadioButton radioButtonWaveOut;
+ private System.Windows.Forms.RadioButton radioButtonWasapi;
+ private System.Windows.Forms.Label label2;
+ }
+}
diff --git a/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.cs b/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.cs
new file mode 100644
index 00000000..dfaf93a6
--- /dev/null
+++ b/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.cs
@@ -0,0 +1,142 @@
+using System;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Windows.Forms;
+using NAudio.Wave;
+
+namespace NAudioDemo.MediaFoundationDemo
+{
+ public partial class MediaFoundationDemoPanel : UserControl
+ {
+ public MediaFoundationDemoPanel()
+ {
+ InitializeComponent();
+ Disposed += OnDisposed;
+ timer1.Interval = 500;
+ }
+
+ private IWavePlayer wavePlayer;
+ private WaveStream reader;
+
+ private void OnDisposed(object sender, EventArgs eventArgs)
+ {
+ if (wavePlayer != null)
+ {
+ wavePlayer.Dispose();
+ wavePlayer = null;
+ }
+ if (reader != null)
+ {
+ reader.Dispose();
+ reader = null;
+ }
+ }
+
+ private void OnTrackBarScroll(object sender, EventArgs e)
+ {
+ if (reader != null)
+ {
+ reader.Position = (trackBar1.Value*reader.Length)/trackBar1.Maximum;
+ }
+ }
+
+ private void OnButtonLoadFileClick(object sender, EventArgs e)
+ {
+
+ if (wavePlayer != null)
+ {
+ wavePlayer.Dispose();
+ wavePlayer = null;
+ }
+
+ var ofd = new OpenFileDialog();
+ if (ofd.ShowDialog() != DialogResult.OK) return;
+
+ if (reader != null)
+ {
+ reader.Dispose();
+ }
+ reader = new MediaFoundationReader(ofd.FileName, new MediaFoundationReader.MediaFoundationReaderSettings() { SingleReaderObject = true});
+ }
+
+ private void OnTimerTick(object sender, EventArgs e)
+ {
+ if (reader != null)
+ {
+ UpdatePosition();
+ }
+ }
+
+ private void UpdatePosition()
+ {
+ labelPosition.Text = string.Format("{0}/{1}", reader.CurrentTime, reader.TotalTime);
+ trackBar1.Value = Math.Min((int) ((trackBar1.Maximum*reader.Position)/reader.Length), trackBar1.Maximum);
+ }
+
+ private void OnButtonPlayClick(object sender, EventArgs e)
+ {
+ timer1.Enabled = true;
+ if (reader == null)
+ {
+ OnButtonLoadFileClick(sender,e);
+ if (reader == null) return;
+ }
+ if (wavePlayer == null)
+ {
+ if (radioButtonWasapi.Checked)
+ {
+ wavePlayer = new WasapiOutGuiThread();
+ }
+ else
+ {
+ wavePlayer = new WaveOut();
+ }
+ wavePlayer.PlaybackStopped += WavePlayerOnPlaybackStopped;
+ wavePlayer.Init(reader);
+ }
+ wavePlayer.Play();
+ }
+
+ private void WavePlayerOnPlaybackStopped(object sender, StoppedEventArgs stoppedEventArgs)
+ {
+ reader.Position = 0;
+ timer1.Enabled = false;
+ UpdatePosition();
+ if (stoppedEventArgs.Exception != null)
+ {
+ MessageBox.Show(stoppedEventArgs.Exception.Message);
+ }
+ }
+
+ private void OnButtonStopClick(object sender, EventArgs e)
+ {
+ if (wavePlayer != null)
+ {
+ wavePlayer.Stop();
+ }
+ }
+
+ private void OnRadioButtonWaveOutCheckedChanged(object sender, EventArgs e)
+ {
+ if (wavePlayer != null)
+ {
+ wavePlayer.Dispose();
+ wavePlayer = null;
+ }
+ }
+ }
+
+ [Export(typeof(INAudioDemoPlugin))]
+ class MediaFoundationDemoPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "Media Foundation Demo"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new MediaFoundationDemoPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.resx b/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.resx
new file mode 100644
index 00000000..f4ed3157
--- /dev/null
+++ b/NAudioDemo/MediaFoundationDemo/MediaFoundationDemoPanel.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ Since the Media Foundation objects are created on STAThread in this WinForms example, the only output models must be ones that marshal calls back onto the GUI thread. Choose between WaveOut with window messages for callbacks or WASAPI using a timer.
+
+
+ 68
+
+
\ No newline at end of file
diff --git a/NAudioDemo/MediaFoundationDemo/WasapiOutGuiThread.cs b/NAudioDemo/MediaFoundationDemo/WasapiOutGuiThread.cs
new file mode 100644
index 00000000..e3653a56
--- /dev/null
+++ b/NAudioDemo/MediaFoundationDemo/WasapiOutGuiThread.cs
@@ -0,0 +1,310 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+using NAudio.CoreAudioApi;
+using NAudio.Wave;
+
+namespace NAudioDemo.MediaFoundationDemo
+{
+ ///
+ /// An experimental example of how WASAPI could be used on a GUI thread
+ /// (to get round STA/MTA threading issues)
+ /// Uses the WinForms timer, for WPF might be best to use the DispatcherTimer
+ ///
+ public class WasapiOutGuiThread : IWavePlayer
+ {
+ private readonly AudioClientShareMode shareMode;
+ private readonly Timer timer;
+ private readonly int latencyMilliseconds;
+ private AudioClient audioClient;
+ private AudioRenderClient renderClient;
+ private IWaveProvider sourceProvider;
+ private int bufferFrameCount;
+ private int bytesPerFrame;
+ private byte[] readBuffer;
+ private PlaybackState playbackState;
+ private WaveFormat outputFormat;
+ private ResamplerDmoStream resamplerDmoStream;
+
+ ///
+ /// Playback Stopped
+ ///
+ public event EventHandler PlaybackStopped;
+
+ ///
+ /// WASAPI Out using default audio endpoint
+ ///
+ public WasapiOutGuiThread() :
+ this(GetDefaultAudioEndpoint(), AudioClientShareMode.Shared, 200)
+ {
+ }
+
+ ///
+ /// Creates a new WASAPI Output device
+ ///
+ /// Device to use
+ /// Share mode to use
+ /// Latency in milliseconds
+ public WasapiOutGuiThread(MMDevice device, AudioClientShareMode shareMode, int latency)
+ {
+ audioClient = device.AudioClient;
+ outputFormat = audioClient.MixFormat;
+ this.shareMode = shareMode;
+ latencyMilliseconds = latency;
+ timer = new Timer();
+ timer.Tick += TimerOnTick;
+ timer.Interval = latency/2;
+ }
+
+ private void TimerOnTick(object sender, EventArgs eventArgs)
+ {
+ if (playbackState == PlaybackState.Playing)
+ {
+ try
+ {
+ // See how much buffer space is available.
+ int numFramesPadding = audioClient.CurrentPadding;
+ int numFramesAvailable = bufferFrameCount - numFramesPadding;
+ if (numFramesAvailable > 0)
+ {
+ FillBuffer(sourceProvider, numFramesAvailable);
+ }
+ }
+ catch (Exception e)
+ {
+ PerformStop(e);
+ }
+ }
+ else if (playbackState == PlaybackState.Stopped)
+ {
+ // user requested stop
+ PerformStop(null);
+ }
+ }
+
+ private void PerformStop(Exception e)
+ {
+ timer.Enabled = false;
+ audioClient.Stop();
+ RaisePlaybackStopped(e);
+ audioClient.Reset();
+ }
+
+ static MMDevice GetDefaultAudioEndpoint()
+ {
+ if (Environment.OSVersion.Version.Major < 6)
+ {
+ throw new NotSupportedException("WASAPI supported only on Windows Vista and above");
+ }
+ var enumerator = new MMDeviceEnumerator();
+ return enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
+ }
+
+ private void RaisePlaybackStopped(Exception e)
+ {
+ var handler = PlaybackStopped;
+ if (handler != null)
+ {
+ handler(this, new StoppedEventArgs(e));
+ }
+ }
+
+ private void FillBuffer(IWaveProvider playbackProvider, int frameCount)
+ {
+ IntPtr buffer = renderClient.GetBuffer(frameCount);
+ int readLength = frameCount * bytesPerFrame;
+ int read = playbackProvider.Read(readBuffer, 0, readLength);
+ if (read == 0)
+ {
+ playbackState = PlaybackState.Stopped;
+ }
+ Marshal.Copy(readBuffer, 0, buffer, read);
+ int actualFrameCount = read / bytesPerFrame;
+ /*if (actualFrameCount != frameCount)
+ {
+ Debug.WriteLine(String.Format("WASAPI wanted {0} frames, supplied {1}", frameCount, actualFrameCount ));
+ }*/
+ renderClient.ReleaseBuffer(actualFrameCount, AudioClientBufferFlags.None);
+ }
+
+ #region IWavePlayer Members
+
+ ///
+ /// Begin Playback
+ ///
+ public void Play()
+ {
+ if (playbackState == PlaybackState.Stopped)
+ {
+ // fill a whole buffer
+ FillBuffer(sourceProvider, bufferFrameCount);
+ audioClient.Start();
+ playbackState = PlaybackState.Playing;
+ timer.Enabled = true;
+ }
+ else if (playbackState == PlaybackState.Paused)
+ {
+ playbackState = PlaybackState.Playing;
+ }
+ }
+
+ ///
+ /// Stop playback and flush buffers
+ ///
+ public void Stop()
+ {
+ playbackState = PlaybackState.Stopped;
+ }
+
+ ///
+ /// Stop playback without flushing buffers
+ ///
+ public void Pause()
+ {
+ if (playbackState == PlaybackState.Playing)
+ {
+ playbackState = PlaybackState.Paused;
+ }
+ }
+
+ ///
+ /// Initialize for playing the specified wave stream
+ ///
+ /// IWaveProvider to play
+ public void Init(IWaveProvider waveProvider)
+ {
+ long latencyRefTimes = latencyMilliseconds * 10000;
+ outputFormat = waveProvider.WaveFormat;
+ // first attempt uses the WaveFormat from the WaveStream
+ WaveFormatExtensible closestSampleRateFormat;
+ if (!audioClient.IsFormatSupported(shareMode, outputFormat, out closestSampleRateFormat))
+ {
+ // Use closesSampleRateFormat (in sharedMode, it equals usualy to the audioClient.MixFormat)
+ // See documentation : http://msdn.microsoft.com/en-us/library/ms678737(VS.85).aspx
+ // They say : "In shared mode, the audio engine always supports the mix format"
+ // The MixFormat is more likely to be a WaveFormatExtensible.
+ if (closestSampleRateFormat == null)
+ {
+ WaveFormat correctSampleRateFormat = audioClient.MixFormat;
+
+ if (!audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ // Iterate from Worst to Best Format
+ WaveFormatExtensible[] bestToWorstFormats = {
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 32,
+ outputFormat.Channels),
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 24,
+ outputFormat.Channels),
+ new WaveFormatExtensible(
+ outputFormat.SampleRate, 16,
+ outputFormat.Channels),
+ };
+
+ // Check from best Format to worst format ( Float32, Int24, Int16 )
+ for (int i = 0; i < bestToWorstFormats.Length; i++)
+ {
+ correctSampleRateFormat = bestToWorstFormats[i];
+ if (audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ break;
+ }
+ correctSampleRateFormat = null;
+ }
+
+ // If still null, then test on the PCM16, 2 channels
+ if (correctSampleRateFormat == null)
+ {
+ // Last Last Last Chance (Thanks WASAPI)
+ correctSampleRateFormat = new WaveFormatExtensible(outputFormat.SampleRate, 16, 2);
+ if (!audioClient.IsFormatSupported(shareMode, correctSampleRateFormat))
+ {
+ throw new NotSupportedException("Can't find a supported format to use");
+ }
+ }
+ }
+ outputFormat = correctSampleRateFormat;
+ }
+ else
+ {
+ outputFormat = closestSampleRateFormat;
+ }
+
+ // just check that we can make it.
+ resamplerDmoStream = new ResamplerDmoStream(waveProvider, outputFormat);
+ sourceProvider = resamplerDmoStream;
+ }
+ else
+ {
+ sourceProvider = waveProvider;
+ }
+
+ // Normal setup for both sharedMode
+ audioClient.Initialize(shareMode, AudioClientStreamFlags.None, latencyRefTimes, 0,
+ outputFormat, Guid.Empty);
+
+
+ // Get the RenderClient
+ renderClient = audioClient.AudioRenderClient;
+
+ // set up the read buffer
+ bufferFrameCount = audioClient.BufferSize;
+ bytesPerFrame = outputFormat.Channels * outputFormat.BitsPerSample / 8;
+ readBuffer = new byte[bufferFrameCount * bytesPerFrame];
+ }
+
+ ///
+ /// Playback State
+ ///
+ public PlaybackState PlaybackState
+ {
+ get { return playbackState; }
+ }
+
+ ///
+ /// Volume
+ ///
+ public float Volume
+ {
+ get
+ {
+ return 1.0f;
+ }
+ set
+ {
+ if (value != 1.0f)
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ ///
+ /// Dispose
+ ///
+ public void Dispose()
+ {
+ if (audioClient != null)
+ {
+ Stop();
+
+ audioClient.Dispose();
+ audioClient = null;
+ renderClient = null;
+ }
+ if (resamplerDmoStream != null)
+ {
+ resamplerDmoStream.Dispose();
+ resamplerDmoStream = null;
+ }
+
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/MidiInDemo/MidiInPanel.Designer.cs b/NAudioDemo/MidiInDemo/MidiInPanel.Designer.cs
new file mode 100644
index 00000000..07ccb150
--- /dev/null
+++ b/NAudioDemo/MidiInDemo/MidiInPanel.Designer.cs
@@ -0,0 +1,179 @@
+namespace NAudioDemo
+{
+ partial class MidiInPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.comboBoxMidiInDevices = new System.Windows.Forms.ComboBox();
+ this.labelDevice = new System.Windows.Forms.Label();
+ this.buttonMonitor = new System.Windows.Forms.Button();
+ this.checkBoxFilterAutoSensing = new System.Windows.Forms.CheckBox();
+ this.progressLog1 = new NAudio.Utils.ProgressLog();
+ this.buttonClearLog = new System.Windows.Forms.Button();
+ this.groupBox1 = new System.Windows.Forms.GroupBox();
+ this.checkBoxMidiOutMessages = new System.Windows.Forms.CheckBox();
+ this.comboBoxMidiOutDevices = new System.Windows.Forms.ComboBox();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.groupBox1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // comboBoxMidiInDevices
+ //
+ this.comboBoxMidiInDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxMidiInDevices.FormattingEnabled = true;
+ this.comboBoxMidiInDevices.Location = new System.Drawing.Point(94, 12);
+ this.comboBoxMidiInDevices.Name = "comboBoxMidiInDevices";
+ this.comboBoxMidiInDevices.Size = new System.Drawing.Size(178, 21);
+ this.comboBoxMidiInDevices.TabIndex = 0;
+ //
+ // labelDevice
+ //
+ this.labelDevice.AutoSize = true;
+ this.labelDevice.Location = new System.Drawing.Point(12, 16);
+ this.labelDevice.Name = "labelDevice";
+ this.labelDevice.Size = new System.Drawing.Size(41, 13);
+ this.labelDevice.TabIndex = 1;
+ this.labelDevice.Text = "Device";
+ //
+ // buttonMonitor
+ //
+ this.buttonMonitor.Location = new System.Drawing.Point(12, 41);
+ this.buttonMonitor.Name = "buttonMonitor";
+ this.buttonMonitor.Size = new System.Drawing.Size(75, 23);
+ this.buttonMonitor.TabIndex = 2;
+ this.buttonMonitor.Text = "Monitor";
+ this.buttonMonitor.UseVisualStyleBackColor = true;
+ this.buttonMonitor.Click += new System.EventHandler(this.buttonMonitor_Click);
+ //
+ // checkBoxFilterAutoSensing
+ //
+ this.checkBoxFilterAutoSensing.AutoSize = true;
+ this.checkBoxFilterAutoSensing.Location = new System.Drawing.Point(384, 47);
+ this.checkBoxFilterAutoSensing.Name = "checkBoxFilterAutoSensing";
+ this.checkBoxFilterAutoSensing.Size = new System.Drawing.Size(114, 17);
+ this.checkBoxFilterAutoSensing.TabIndex = 4;
+ this.checkBoxFilterAutoSensing.Text = "Filter Auto-Sensing";
+ this.checkBoxFilterAutoSensing.UseVisualStyleBackColor = true;
+ //
+ // progressLog1
+ //
+ this.progressLog1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.progressLog1.BackColor = System.Drawing.SystemColors.ControlDarkDark;
+ this.progressLog1.Location = new System.Drawing.Point(12, 70);
+ this.progressLog1.Name = "progressLog1";
+ this.progressLog1.Padding = new System.Windows.Forms.Padding(1);
+ this.progressLog1.Size = new System.Drawing.Size(486, 258);
+ this.progressLog1.TabIndex = 3;
+ //
+ // buttonClearLog
+ //
+ this.buttonClearLog.Location = new System.Drawing.Point(94, 41);
+ this.buttonClearLog.Name = "buttonClearLog";
+ this.buttonClearLog.Size = new System.Drawing.Size(75, 23);
+ this.buttonClearLog.TabIndex = 5;
+ this.buttonClearLog.Text = "Clear Log";
+ this.buttonClearLog.UseVisualStyleBackColor = true;
+ this.buttonClearLog.Click += new System.EventHandler(this.buttonClearLog_Click);
+ //
+ // groupBox1
+ //
+ this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.groupBox1.Controls.Add(this.checkBoxMidiOutMessages);
+ this.groupBox1.Controls.Add(this.comboBoxMidiOutDevices);
+ this.groupBox1.Location = new System.Drawing.Point(13, 343);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new System.Drawing.Size(484, 45);
+ this.groupBox1.TabIndex = 6;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "MIDI Out";
+ //
+ // checkBoxMidiOutMessages
+ //
+ this.checkBoxMidiOutMessages.AutoSize = true;
+ this.checkBoxMidiOutMessages.Location = new System.Drawing.Point(6, 19);
+ this.checkBoxMidiOutMessages.Name = "checkBoxMidiOutMessages";
+ this.checkBoxMidiOutMessages.Size = new System.Drawing.Size(172, 17);
+ this.checkBoxMidiOutMessages.TabIndex = 0;
+ this.checkBoxMidiOutMessages.Text = "Send Test MIDI Out Messages";
+ this.checkBoxMidiOutMessages.UseVisualStyleBackColor = true;
+ //
+ // comboBoxMidiOutDevices
+ //
+ this.comboBoxMidiOutDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxMidiOutDevices.FormattingEnabled = true;
+ this.comboBoxMidiOutDevices.Location = new System.Drawing.Point(196, 17);
+ this.comboBoxMidiOutDevices.Name = "comboBoxMidiOutDevices";
+ this.comboBoxMidiOutDevices.Size = new System.Drawing.Size(178, 21);
+ this.comboBoxMidiOutDevices.TabIndex = 0;
+ //
+ // timer1
+ //
+ this.timer1.Enabled = true;
+ this.timer1.Interval = 1000;
+ this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // MidiInForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(510, 399);
+ this.Controls.Add(this.groupBox1);
+ this.Controls.Add(this.buttonClearLog);
+ this.Controls.Add(this.checkBoxFilterAutoSensing);
+ this.Controls.Add(this.progressLog1);
+ this.Controls.Add(this.buttonMonitor);
+ this.Controls.Add(this.labelDevice);
+ this.Controls.Add(this.comboBoxMidiInDevices);
+ this.Name = "MidiInForm";
+ this.Text = "MIDI In Sample";
+ this.Disposed += this.MidiInPanel_Disposed;
+ this.Load += new System.EventHandler(this.MidiInForm_Load);
+ this.groupBox1.ResumeLayout(false);
+ this.groupBox1.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.ComboBox comboBoxMidiInDevices;
+ private System.Windows.Forms.Label labelDevice;
+ private System.Windows.Forms.Button buttonMonitor;
+ private NAudio.Utils.ProgressLog progressLog1;
+ private System.Windows.Forms.CheckBox checkBoxFilterAutoSensing;
+ private System.Windows.Forms.Button buttonClearLog;
+ private System.Windows.Forms.GroupBox groupBox1;
+ private System.Windows.Forms.CheckBox checkBoxMidiOutMessages;
+ private System.Windows.Forms.ComboBox comboBoxMidiOutDevices;
+ private System.Windows.Forms.Timer timer1;
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/MidiInDemo/MidiInPanel.cs b/NAudioDemo/MidiInDemo/MidiInPanel.cs
new file mode 100644
index 00000000..71ba19e9
--- /dev/null
+++ b/NAudioDemo/MidiInDemo/MidiInPanel.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.Midi;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo
+{
+ public partial class MidiInPanel : UserControl
+ {
+ MidiIn midiIn;
+ MidiOut midiOut;
+ bool monitoring;
+ List events;
+ int midiOutIndex;
+
+ public MidiInPanel()
+ {
+ InitializeComponent();
+ }
+
+ private void MidiInForm_Load(object sender, EventArgs e)
+ {
+ for (int device = 0; device < MidiIn.NumberOfDevices; device++)
+ {
+ comboBoxMidiInDevices.Items.Add(MidiIn.DeviceInfo(device).ProductName);
+ }
+ if (comboBoxMidiInDevices.Items.Count > 0)
+ {
+ comboBoxMidiInDevices.SelectedIndex = 0;
+ }
+ for (int device = 0; device < MidiOut.NumberOfDevices; device++)
+ {
+ comboBoxMidiOutDevices.Items.Add(MidiOut.DeviceInfo(device).ProductName);
+ }
+ if (comboBoxMidiOutDevices.Items.Count > 0)
+ {
+ comboBoxMidiOutDevices.SelectedIndex = 0;
+ }
+ events = new List();
+ for (int note = 50; note < 62; note++)
+ {
+ AddNoteEvent(note);
+ }
+ }
+
+ private void AddNoteEvent(int noteNumber)
+ {
+ int channel = 2;
+ NoteOnEvent noteOnEvent = new NoteOnEvent(0, channel, noteNumber, 100, 50);
+ events.Add(noteOnEvent);
+ events.Add(noteOnEvent.OffEvent);
+ }
+
+ private void buttonMonitor_Click(object sender, EventArgs e)
+ {
+ if (!monitoring)
+ {
+ StartMonitoring();
+ }
+ else
+ {
+ StopMonitoring();
+ }
+ }
+
+ private void StartMonitoring()
+ {
+ if (comboBoxMidiInDevices.Items.Count == 0)
+ {
+ MessageBox.Show("No MIDI input devices available");
+ return;
+ }
+ if (midiIn != null)
+ {
+ midiIn.Dispose();
+ midiIn.MessageReceived -= midiIn_MessageReceived;
+ midiIn.ErrorReceived -= midiIn_ErrorReceived;
+ midiIn = null;
+ }
+ if (midiIn == null)
+ {
+ midiIn = new MidiIn(comboBoxMidiInDevices.SelectedIndex);
+ midiIn.MessageReceived += midiIn_MessageReceived;
+ midiIn.ErrorReceived += midiIn_ErrorReceived;
+ }
+ midiIn.Start();
+ monitoring = true;
+ buttonMonitor.Text = "Stop";
+ comboBoxMidiInDevices.Enabled = false;
+ }
+
+ void midiIn_ErrorReceived(object sender, MidiInMessageEventArgs e)
+ {
+ progressLog1.LogMessage(Color.Red, String.Format("Time {0} Message 0x{1:X8} Event {2}",
+ e.Timestamp, e.RawMessage, e.MidiEvent));
+ }
+
+ private void StopMonitoring()
+ {
+ if (monitoring)
+ {
+ midiIn.Stop();
+ monitoring = false;
+ buttonMonitor.Text = "Monitor";
+ comboBoxMidiInDevices.Enabled = true;
+ }
+ }
+
+ void midiIn_MessageReceived(object sender, MidiInMessageEventArgs e)
+ {
+ if (checkBoxFilterAutoSensing.Checked && e.MidiEvent != null && e.MidiEvent.CommandCode == MidiCommandCode.AutoSensing)
+ {
+ return;
+ }
+ progressLog1.LogMessage(Color.Blue, String.Format("Time {0} Message 0x{1:X8} Event {2}",
+ e.Timestamp, e.RawMessage, e.MidiEvent));
+ }
+
+ private void MidiInPanel_Disposed(object sender, EventArgs e)
+ {
+ timer1.Enabled = false;
+ StopMonitoring();
+ if (midiIn != null)
+ {
+ midiIn.Dispose();
+ midiIn = null;
+ }
+ if (midiOut != null)
+ {
+ midiOut.Dispose();
+ midiOut = null;
+ }
+ }
+
+ private void buttonClearLog_Click(object sender, EventArgs e)
+ {
+ progressLog1.ClearLog();
+ }
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ if (checkBoxMidiOutMessages.Checked)
+ {
+ SendNextMidiOutMessage();
+ }
+ }
+
+ private void SendNextMidiOutMessage()
+ {
+ if (midiOut == null)
+ {
+ midiOut = new MidiOut(comboBoxMidiOutDevices.SelectedIndex);
+ }
+ MidiEvent eventToSend = events[midiOutIndex++];
+ midiOut.Send(eventToSend.GetAsShortMessage());
+ progressLog1.LogMessage(Color.Green, String.Format("Sent {0}", eventToSend));
+ if (midiOutIndex >= events.Count)
+ {
+ midiOutIndex = 0;
+ }
+ }
+ }
+
+ [Export(typeof(INAudioDemoPlugin))]
+ public class MidiInPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "MIDI In"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new MidiInPanel();
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/MidiInDemo/MidiInPanel.resx b/NAudioDemo/MidiInDemo/MidiInPanel.resx
new file mode 100644
index 00000000..93f75a97
--- /dev/null
+++ b/NAudioDemo/MidiInDemo/MidiInPanel.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.Designer.cs b/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.Designer.cs
new file mode 100644
index 00000000..1479f222
--- /dev/null
+++ b/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.Designer.cs
@@ -0,0 +1,179 @@
+namespace NAudioDemo
+{
+ partial class Mp3StreamingPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.buttonPlay = new System.Windows.Forms.Button();
+ this.textBoxStreamingUrl = new System.Windows.Forms.TextBox();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.label1 = new System.Windows.Forms.Label();
+ this.progressBarBuffer = new System.Windows.Forms.ProgressBar();
+ this.label2 = new System.Windows.Forms.Label();
+ this.buttonPause = new System.Windows.Forms.Button();
+ this.buttonStop = new System.Windows.Forms.Button();
+ this.labelBuffered = new System.Windows.Forms.Label();
+ this.labelVolume = new System.Windows.Forms.Label();
+ this.volumeSlider1 = new NAudio.Gui.VolumeSlider();
+ this.SuspendLayout();
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.Location = new System.Drawing.Point(12, 98);
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(75, 23);
+ this.buttonPlay.TabIndex = 0;
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.UseVisualStyleBackColor = true;
+ this.buttonPlay.Click += new System.EventHandler(this.buttonPlay_Click);
+ //
+ // textBoxStreamingUrl
+ //
+ this.textBoxStreamingUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.textBoxStreamingUrl.Location = new System.Drawing.Point(97, 12);
+ this.textBoxStreamingUrl.Name = "textBoxStreamingUrl";
+ this.textBoxStreamingUrl.Size = new System.Drawing.Size(228, 20);
+ this.textBoxStreamingUrl.TabIndex = 1;
+ this.textBoxStreamingUrl.Text = "http://radio.reaper.fm/stream/";
+ //
+ // timer1
+ //
+ this.timer1.Interval = 250;
+ this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(12, 15);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(82, 13);
+ this.label1.TabIndex = 2;
+ this.label1.Text = "Streaming URL:";
+ //
+ // progressBarBuffer
+ //
+ this.progressBarBuffer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.progressBarBuffer.Location = new System.Drawing.Point(97, 39);
+ this.progressBarBuffer.Name = "progressBarBuffer";
+ this.progressBarBuffer.Size = new System.Drawing.Size(195, 23);
+ this.progressBarBuffer.TabIndex = 3;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(12, 45);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(50, 13);
+ this.label2.TabIndex = 4;
+ this.label2.Text = "Buffered:";
+ //
+ // buttonPause
+ //
+ this.buttonPause.Location = new System.Drawing.Point(94, 98);
+ this.buttonPause.Name = "buttonPause";
+ this.buttonPause.Size = new System.Drawing.Size(75, 23);
+ this.buttonPause.TabIndex = 5;
+ this.buttonPause.Text = "Pause";
+ this.buttonPause.UseVisualStyleBackColor = true;
+ this.buttonPause.Click += new System.EventHandler(this.buttonPause_Click);
+ //
+ // buttonStop
+ //
+ this.buttonStop.Location = new System.Drawing.Point(176, 98);
+ this.buttonStop.Name = "buttonStop";
+ this.buttonStop.Size = new System.Drawing.Size(75, 23);
+ this.buttonStop.TabIndex = 6;
+ this.buttonStop.Text = "Stop";
+ this.buttonStop.UseVisualStyleBackColor = true;
+ this.buttonStop.Click += new System.EventHandler(this.buttonStop_Click);
+ //
+ // labelBuffered
+ //
+ this.labelBuffered.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.labelBuffered.AutoSize = true;
+ this.labelBuffered.Location = new System.Drawing.Point(298, 45);
+ this.labelBuffered.Name = "labelBuffered";
+ this.labelBuffered.Size = new System.Drawing.Size(27, 13);
+ this.labelBuffered.TabIndex = 7;
+ this.labelBuffered.Text = "0.0s";
+ //
+ // labelVolume
+ //
+ this.labelVolume.AutoSize = true;
+ this.labelVolume.Location = new System.Drawing.Point(12, 73);
+ this.labelVolume.Name = "labelVolume";
+ this.labelVolume.Size = new System.Drawing.Size(45, 13);
+ this.labelVolume.TabIndex = 8;
+ this.labelVolume.Text = "Volume:";
+ //
+ // volumeSlider1
+ //
+ this.volumeSlider1.Location = new System.Drawing.Point(97, 69);
+ this.volumeSlider1.Name = "volumeSlider1";
+ this.volumeSlider1.Size = new System.Drawing.Size(109, 19);
+ this.volumeSlider1.TabIndex = 9;
+ this.volumeSlider1.Volume = 1F;
+ //
+ // MP3StreamingPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.volumeSlider1);
+ this.Controls.Add(this.labelVolume);
+ this.Controls.Add(this.labelBuffered);
+ this.Controls.Add(this.buttonStop);
+ this.Controls.Add(this.buttonPause);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.progressBarBuffer);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.textBoxStreamingUrl);
+ this.Controls.Add(this.buttonPlay);
+ this.Name = "Mp3StreamingPanel";
+ this.Size = new System.Drawing.Size(337, 133);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button buttonPlay;
+ private System.Windows.Forms.TextBox textBoxStreamingUrl;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.ProgressBar progressBarBuffer;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Button buttonPause;
+ private System.Windows.Forms.Button buttonStop;
+ private System.Windows.Forms.Label labelBuffered;
+ private System.Windows.Forms.Label labelVolume;
+ private NAudio.Gui.VolumeSlider volumeSlider1;
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.cs b/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.cs
new file mode 100644
index 00000000..2056da15
--- /dev/null
+++ b/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.cs
@@ -0,0 +1,300 @@
+using System;
+using System.Windows.Forms;
+using NAudio.Wave;
+using System.Net;
+using System.Threading;
+using System.IO;
+using System.Diagnostics;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo
+{
+ public partial class Mp3StreamingPanel : UserControl
+ {
+ enum StreamingPlaybackState
+ {
+ Stopped,
+ Playing,
+ Buffering,
+ Paused
+ }
+
+ public Mp3StreamingPanel()
+ {
+ InitializeComponent();
+ volumeSlider1.VolumeChanged += OnVolumeSliderChanged;
+ Disposed += MP3StreamingPanel_Disposing;
+ }
+
+ void OnVolumeSliderChanged(object sender, EventArgs e)
+ {
+ if (volumeProvider != null)
+ {
+ volumeProvider.Volume = volumeSlider1.Volume;
+ }
+ }
+
+ private BufferedWaveProvider bufferedWaveProvider;
+ private IWavePlayer waveOut;
+ private volatile StreamingPlaybackState playbackState;
+ private volatile bool fullyDownloaded;
+ private HttpWebRequest webRequest;
+ private VolumeWaveProvider16 volumeProvider;
+
+ delegate void ShowErrorDelegate(string message);
+
+ private void ShowError(string message)
+ {
+ if (InvokeRequired)
+ {
+ BeginInvoke(new ShowErrorDelegate(ShowError), message);
+ }
+ else
+ {
+ MessageBox.Show(message);
+ }
+ }
+
+ private void StreamMp3(object state)
+ {
+ fullyDownloaded = false;
+ var url = (string)state;
+ webRequest = (HttpWebRequest)WebRequest.Create(url);
+ HttpWebResponse resp;
+ try
+ {
+ resp = (HttpWebResponse)webRequest.GetResponse();
+ }
+ catch(WebException e)
+ {
+ if (e.Status != WebExceptionStatus.RequestCanceled)
+ {
+ ShowError(e.Message);
+ }
+ return;
+ }
+ var buffer = new byte[16384 * 4]; // needs to be big enough to hold a decompressed frame
+
+ IMp3FrameDecompressor decompressor = null;
+ try
+ {
+ using (var responseStream = resp.GetResponseStream())
+ {
+ var readFullyStream = new ReadFullyStream(responseStream);
+ do
+ {
+ if (IsBufferNearlyFull)
+ {
+ Debug.WriteLine("Buffer getting full, taking a break");
+ Thread.Sleep(500);
+ }
+ else
+ {
+ Mp3Frame frame;
+ try
+ {
+ frame = Mp3Frame.LoadFromStream(readFullyStream);
+ }
+ catch (EndOfStreamException)
+ {
+ fullyDownloaded = true;
+ // reached the end of the MP3 file / stream
+ break;
+ }
+ catch (WebException)
+ {
+ // probably we have aborted download from the GUI thread
+ break;
+ }
+ if (decompressor == null)
+ {
+ // don't think these details matter too much - just help ACM select the right codec
+ // however, the buffered provider doesn't know what sample rate it is working at
+ // until we have a frame
+ decompressor = CreateFrameDecompressor(frame);
+ bufferedWaveProvider = new BufferedWaveProvider(decompressor.OutputFormat);
+ bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(20); // allow us to get well ahead of ourselves
+ //this.bufferedWaveProvider.BufferedDuration = 250;
+ }
+ int decompressed = decompressor.DecompressFrame(frame, buffer, 0);
+ //Debug.WriteLine(String.Format("Decompressed a frame {0}", decompressed));
+ bufferedWaveProvider.AddSamples(buffer, 0, decompressed);
+ }
+
+ } while (playbackState != StreamingPlaybackState.Stopped);
+ Debug.WriteLine("Exiting");
+ // was doing this in a finally block, but for some reason
+ // we are hanging on response stream .Dispose so never get there
+ decompressor.Dispose();
+ }
+ }
+ finally
+ {
+ if (decompressor != null)
+ {
+ decompressor.Dispose();
+ }
+ }
+ }
+
+ private static IMp3FrameDecompressor CreateFrameDecompressor(Mp3Frame frame)
+ {
+ WaveFormat waveFormat = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2,
+ frame.FrameLength, frame.BitRate);
+ return new AcmMp3FrameDecompressor(waveFormat);
+ }
+
+ private bool IsBufferNearlyFull
+ {
+ get
+ {
+ return bufferedWaveProvider != null &&
+ bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes
+ < bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4;
+ }
+ }
+
+ private void buttonPlay_Click(object sender, EventArgs e)
+ {
+ if (playbackState == StreamingPlaybackState.Stopped)
+ {
+ playbackState = StreamingPlaybackState.Buffering;
+ bufferedWaveProvider = null;
+ ThreadPool.QueueUserWorkItem(StreamMp3, textBoxStreamingUrl.Text);
+ timer1.Enabled = true;
+ }
+ else if (playbackState == StreamingPlaybackState.Paused)
+ {
+ playbackState = StreamingPlaybackState.Buffering;
+ }
+ }
+
+ private void StopPlayback()
+ {
+ if (playbackState != StreamingPlaybackState.Stopped)
+ {
+ if (!fullyDownloaded)
+ {
+ webRequest.Abort();
+ }
+
+ playbackState = StreamingPlaybackState.Stopped;
+ if (waveOut != null)
+ {
+ waveOut.Stop();
+ waveOut.Dispose();
+ waveOut = null;
+ }
+ timer1.Enabled = false;
+ // n.b. streaming thread may not yet have exited
+ Thread.Sleep(500);
+ ShowBufferState(0);
+ }
+ }
+
+ private void ShowBufferState(double totalSeconds)
+ {
+ labelBuffered.Text = String.Format("{0:0.0}s", totalSeconds);
+ progressBarBuffer.Value = (int)(totalSeconds * 1000);
+ }
+
+ private void timer1_Tick(object sender, EventArgs e)
+ {
+ if (playbackState != StreamingPlaybackState.Stopped)
+ {
+ if (waveOut == null && bufferedWaveProvider != null)
+ {
+ Debug.WriteLine("Creating WaveOut Device");
+ waveOut = CreateWaveOut();
+ waveOut.PlaybackStopped += OnPlaybackStopped;
+ volumeProvider = new VolumeWaveProvider16(bufferedWaveProvider);
+ volumeProvider.Volume = volumeSlider1.Volume;
+ waveOut.Init(volumeProvider);
+ progressBarBuffer.Maximum = (int)bufferedWaveProvider.BufferDuration.TotalMilliseconds;
+ }
+ else if (bufferedWaveProvider != null)
+ {
+ var bufferedSeconds = bufferedWaveProvider.BufferedDuration.TotalSeconds;
+ ShowBufferState(bufferedSeconds);
+ // make it stutter less if we buffer up a decent amount before playing
+ if (bufferedSeconds < 0.5 && playbackState == StreamingPlaybackState.Playing && !fullyDownloaded)
+ {
+ Pause();
+ }
+ else if (bufferedSeconds > 4 && playbackState == StreamingPlaybackState.Buffering)
+ {
+ Play();
+ }
+ else if (fullyDownloaded && bufferedSeconds == 0)
+ {
+ Debug.WriteLine("Reached end of stream");
+ StopPlayback();
+ }
+ }
+
+ }
+ }
+
+ private void Play()
+ {
+ waveOut.Play();
+ Debug.WriteLine(String.Format("Started playing, waveOut.PlaybackState={0}", waveOut.PlaybackState));
+ playbackState = StreamingPlaybackState.Playing;
+ }
+
+ private void Pause()
+ {
+ playbackState = StreamingPlaybackState.Buffering;
+ waveOut.Pause();
+ Debug.WriteLine(String.Format("Paused to buffer, waveOut.PlaybackState={0}", waveOut.PlaybackState));
+ }
+
+ private IWavePlayer CreateWaveOut()
+ {
+ return new WaveOut();
+ }
+
+ private void MP3StreamingPanel_Disposing(object sender, EventArgs e)
+ {
+ StopPlayback();
+ }
+
+ private void buttonPause_Click(object sender, EventArgs e)
+ {
+ if (playbackState == StreamingPlaybackState.Playing || playbackState == StreamingPlaybackState.Buffering)
+ {
+ waveOut.Pause();
+ Debug.WriteLine(String.Format("User requested Pause, waveOut.PlaybackState={0}", waveOut.PlaybackState));
+ playbackState = StreamingPlaybackState.Paused;
+ }
+ }
+
+ private void buttonStop_Click(object sender, EventArgs e)
+ {
+ StopPlayback();
+ }
+
+ private void OnPlaybackStopped(object sender, StoppedEventArgs e)
+ {
+ Debug.WriteLine("Playback Stopped");
+ if (e.Exception != null)
+ {
+ MessageBox.Show(String.Format("Playback Error {0}", e.Exception.Message));
+ }
+ }
+ }
+
+ [Export(typeof(INAudioDemoPlugin))]
+ public class Mp3StreamingPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "MP3 Streaming"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new Mp3StreamingPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.resx b/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.resx
new file mode 100644
index 00000000..6999fbfa
--- /dev/null
+++ b/NAudioDemo/Mp3StreamingDemo/MP3StreamingPanel.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/NAudioDemo/Mp3StreamingDemo/ReadFullyStream.cs b/NAudioDemo/Mp3StreamingDemo/ReadFullyStream.cs
new file mode 100644
index 00000000..69f445b5
--- /dev/null
+++ b/NAudioDemo/Mp3StreamingDemo/ReadFullyStream.cs
@@ -0,0 +1,101 @@
+using System;
+using System.IO;
+
+namespace NAudioDemo
+{
+ public class ReadFullyStream : Stream
+ {
+ private readonly Stream sourceStream;
+ private long pos; // psuedo-position
+ private readonly byte[] readAheadBuffer;
+ private int readAheadLength;
+ private int readAheadOffset;
+
+ public ReadFullyStream(Stream sourceStream)
+ {
+ this.sourceStream = sourceStream;
+ readAheadBuffer = new byte[4096];
+ }
+ public override bool CanRead
+ {
+ get { return true; }
+ }
+
+ public override bool CanSeek
+ {
+ get { return false; }
+ }
+
+ public override bool CanWrite
+ {
+ get { return false; }
+ }
+
+ public override void Flush()
+ {
+ throw new InvalidOperationException();
+ }
+
+ public override long Length
+ {
+ get { return pos; }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ return pos;
+ }
+ set
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int bytesRead = 0;
+ while (bytesRead < count)
+ {
+ int readAheadAvailableBytes = readAheadLength - readAheadOffset;
+ int bytesRequired = count - bytesRead;
+ if (readAheadAvailableBytes > 0)
+ {
+ int toCopy = Math.Min(readAheadAvailableBytes, bytesRequired);
+ Array.Copy(readAheadBuffer, readAheadOffset, buffer, offset + bytesRead, toCopy);
+ bytesRead += toCopy;
+ readAheadOffset += toCopy;
+ }
+ else
+ {
+ readAheadOffset = 0;
+ readAheadLength = sourceStream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
+ //Debug.WriteLine(String.Format("Read {0} bytes (requested {1})", readAheadLength, readAheadBuffer.Length));
+ if (readAheadLength == 0)
+ {
+ break;
+ }
+ }
+ }
+ pos += bytesRead;
+ return bytesRead;
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new InvalidOperationException();
+ }
+ }
+}
diff --git a/NAudioDemo/NAudioDemo.csproj b/NAudioDemo/NAudioDemo.csproj
new file mode 100644
index 00000000..f6972f8f
--- /dev/null
+++ b/NAudioDemo/NAudioDemo.csproj
@@ -0,0 +1,347 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {C37A547B-F31E-45FB-870A-CFA704D06152}
+ WinExe
+ Properties
+ NAudioDemo
+ NAudioDemo
+
+
+
+
+ 3.5
+ v3.5
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+ Client
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AllRules.ruleset
+ x86
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AllRules.ruleset
+ x86
+
+
+
+ ..\Lib\MEF\Microsoft.ComponentModel.Composition.Initialization.Desktop.dll
+
+
+ ..\Lib\NSpeex\NSpeex.dll
+
+
+
+ ..\Lib\MEF\System.ComponentModel.Composition.dll
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ AcmPanel.cs
+
+
+ UserControl
+
+
+ AsioDirectPanel.cs
+
+
+ UserControl
+
+
+ AsioRecordingPanel.cs
+
+
+
+ UserControl
+
+
+ AsioOutSettingsPanel.cs
+
+
+ UserControl
+
+
+ AudioPlaybackPanel.cs
+
+
+
+ UserControl
+
+
+ DirectSoundOutSettingsPanel.cs
+
+
+
+
+
+ UserControl
+
+
+ WasapiOutSettingsPanel.cs
+
+
+
+ UserControl
+
+
+ WaveOutSettingsPanel.cs
+
+
+
+ Images.resx
+ True
+ True
+
+
+
+
+ Form
+
+
+ MainForm.cs
+
+
+ UserControl
+
+
+ MediaFoundationDemoPanel.cs
+
+
+
+ UserControl
+
+
+ MidiInPanel.cs
+
+
+ UserControl
+
+
+ MP3StreamingPanel.cs
+
+
+
+
+
+
+
+
+
+ UserControl
+
+
+ NetworkChatPanel.cs
+
+
+
+
+
+ UserControl
+
+
+ FadeInOutPanel.cs
+
+
+ UserControl
+
+
+ GeneratorPanel.cs
+
+
+
+ UserControl
+
+
+ SimplePlaybackPanel.cs
+
+
+
+
+
+ AcmPanel.cs
+ Designer
+
+
+ AsioDirectPanel.cs
+
+
+ AsioRecordingPanel.cs
+
+
+ AsioOutSettingsPanel.cs
+
+
+ AudioPlaybackPanel.cs
+ Designer
+
+
+ DirectSoundOutSettingsPanel.cs
+
+
+ WasapiOutSettingsPanel.cs
+
+
+ WaveOutSettingsPanel.cs
+
+
+ ResXFileCodeGenerator
+ Images.Designer.cs
+ Designer
+
+
+ Designer
+ MainForm.cs
+
+
+ MediaFoundationDemoPanel.cs
+
+
+ Designer
+ MidiInPanel.cs
+
+
+ MP3StreamingPanel.cs
+
+
+ NetworkChatPanel.cs
+
+
+ FadeInOutPanel.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ RecordingPanel.cs
+ Designer
+
+
+ True
+ Resources.resx
+ True
+
+
+ GeneratorPanel.cs
+
+
+ SimplePlaybackPanel.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+ UserControl
+
+
+ RecordingPanel.cs
+
+
+
+
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}
+ NAudio
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudioDemo/NAudioDemo.csproj.DotSettings b/NAudioDemo/NAudioDemo.csproj.DotSettings
new file mode 100644
index 00000000..6e7fff86
--- /dev/null
+++ b/NAudioDemo/NAudioDemo.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ No
\ No newline at end of file
diff --git a/NAudioDemo/NAudioDemo.csproj.user b/NAudioDemo/NAudioDemo.csproj.user
new file mode 100644
index 00000000..566c009a
--- /dev/null
+++ b/NAudioDemo/NAudioDemo.csproj.user
@@ -0,0 +1,6 @@
+
+
+
+ ShowAllFiles
+
+
\ No newline at end of file
diff --git a/NAudioDemo/NetworkChatDemo/ALawChatCodec.cs b/NAudioDemo/NetworkChatDemo/ALawChatCodec.cs
new file mode 100644
index 00000000..40ad1af1
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/ALawChatCodec.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using NAudio.Codecs;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ class AcmALawChatCodec : AcmChatCodec
+ {
+ public AcmALawChatCodec()
+ : base(new WaveFormat(8000, 16, 1), WaveFormat.CreateALawFormat(8000, 1))
+ {
+ }
+
+ public override string Name
+ {
+ get { return "ACM G.711 a-law"; }
+ }
+ }
+
+
+ [Export(typeof(INetworkChatCodec))]
+ class ALawChatCodec : INetworkChatCodec
+ {
+ public string Name
+ {
+ get { return "G.711 a-law"; }
+ }
+
+ public int BitsPerSecond
+ {
+ get { return RecordFormat.SampleRate * 8; }
+ }
+
+ public WaveFormat RecordFormat
+ {
+ get { return new WaveFormat(8000, 16, 1); }
+ }
+
+ public byte[] Encode(byte[] data, int offset, int length)
+ {
+ byte[] encoded = new byte[length / 2];
+ int outIndex = 0;
+ for (int n = 0; n < length; n += 2)
+ {
+ encoded[outIndex++] = ALawEncoder.LinearToALawSample(BitConverter.ToInt16(data, offset + n));
+ }
+ return encoded;
+ }
+
+ public byte[] Decode(byte[] data, int offset, int length)
+ {
+ byte[] decoded = new byte[length * 2];
+ int outIndex = 0;
+ for (int n = 0; n < length; n++)
+ {
+ short decodedSample = ALawDecoder.ALawToLinearSample(data[n + offset]);
+ decoded[outIndex++] = (byte)(decodedSample & 0xFF);
+ decoded[outIndex++] = (byte)(decodedSample >> 8);
+ }
+ return decoded;
+ }
+
+ public void Dispose()
+ {
+ // nothing to do
+ }
+
+ public bool IsAvailable { get { return true; } }
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/AcmChatCodec.cs b/NAudioDemo/NetworkChatDemo/AcmChatCodec.cs
new file mode 100644
index 00000000..4a8832f7
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/AcmChatCodec.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using NAudio.Wave.Compression;
+using NAudio;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ ///
+ /// useful base class for deriving any chat codecs that will use ACM for decode and encode
+ ///
+ abstract class AcmChatCodec : INetworkChatCodec
+ {
+ private readonly WaveFormat encodeFormat;
+ private AcmStream encodeStream;
+ private AcmStream decodeStream;
+ private int decodeSourceBytesLeftovers;
+ private int encodeSourceBytesLeftovers;
+
+ protected AcmChatCodec(WaveFormat recordFormat, WaveFormat encodeFormat)
+ {
+ RecordFormat = recordFormat;
+ this.encodeFormat = encodeFormat;
+ }
+
+ public WaveFormat RecordFormat { get; private set; }
+
+ public byte[] Encode(byte[] data, int offset, int length)
+ {
+ if (encodeStream == null)
+ {
+ encodeStream = new AcmStream(RecordFormat, encodeFormat);
+ }
+ //Debug.WriteLine(String.Format("Encoding {0} + {1} bytes", length, encodeSourceBytesLeftovers));
+ return Convert(encodeStream, data, offset, length, ref encodeSourceBytesLeftovers);
+ }
+
+ public byte[] Decode(byte[] data, int offset, int length)
+ {
+ if (decodeStream == null)
+ {
+ decodeStream = new AcmStream(encodeFormat, RecordFormat);
+ }
+ //Debug.WriteLine(String.Format("Decoding {0} + {1} bytes", data.Length, decodeSourceBytesLeftovers));
+ return Convert(decodeStream, data, offset, length, ref decodeSourceBytesLeftovers);
+ }
+
+ private static byte[] Convert(AcmStream conversionStream, byte[] data, int offset, int length, ref int sourceBytesLeftovers)
+ {
+ int bytesInSourceBuffer = length + sourceBytesLeftovers;
+ Array.Copy(data, offset, conversionStream.SourceBuffer, sourceBytesLeftovers, length);
+ int sourceBytesConverted;
+ int bytesConverted = conversionStream.Convert(bytesInSourceBuffer, out sourceBytesConverted);
+ sourceBytesLeftovers = bytesInSourceBuffer - sourceBytesConverted;
+ if (sourceBytesLeftovers > 0)
+ {
+ //Debug.WriteLine(String.Format("Asked for {0}, converted {1}", bytesInSourceBuffer, sourceBytesConverted));
+ // shift the leftovers down
+ Array.Copy(conversionStream.SourceBuffer, sourceBytesConverted, conversionStream.SourceBuffer, 0, sourceBytesLeftovers);
+ }
+ byte[] encoded = new byte[bytesConverted];
+ Array.Copy(conversionStream.DestBuffer, 0, encoded, 0, bytesConverted);
+ return encoded;
+ }
+
+ public abstract string Name { get; }
+
+ public int BitsPerSecond
+ {
+ get
+ {
+ return encodeFormat.AverageBytesPerSecond * 8;
+ }
+ }
+
+ public void Dispose()
+ {
+ if (encodeStream != null)
+ {
+ encodeStream.Dispose();
+ encodeStream = null;
+ }
+ if (decodeStream != null)
+ {
+ decodeStream.Dispose();
+ decodeStream = null;
+ }
+ }
+
+ public bool IsAvailable
+ {
+ get
+ {
+ // determine if this codec is installed on this PC
+ bool available = true;
+ try
+ {
+ using (new AcmStream(RecordFormat, encodeFormat)) { }
+ using (new AcmStream(encodeFormat, RecordFormat)) { }
+ }
+ catch (MmException)
+ {
+ available = false;
+ }
+ return available;
+ }
+ }
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/G722ChatCodec.cs b/NAudioDemo/NetworkChatDemo/G722ChatCodec.cs
new file mode 100644
index 00000000..21e40195
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/G722ChatCodec.cs
@@ -0,0 +1,78 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using NAudio.Codecs;
+using System.Diagnostics;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ [Export(typeof(INetworkChatCodec))]
+ class G722ChatCodec : INetworkChatCodec
+ {
+ private readonly int bitrate;
+ private readonly G722CodecState encoderState;
+ private readonly G722CodecState decoderState;
+ private readonly WaveFormat recordingFormat;
+ private readonly G722Codec codec;
+
+ public G722ChatCodec()
+ {
+ bitrate = 64000;
+ encoderState = new G722CodecState(bitrate, G722Flags.None);
+ decoderState = new G722CodecState(bitrate, G722Flags.None);
+ codec = new G722Codec();
+ recordingFormat = new WaveFormat(16000, 1);
+ }
+
+ public string Name
+ {
+ get { return "G.722 16kHz"; }
+ }
+
+ public int BitsPerSecond
+ {
+ get { return bitrate; }
+ }
+
+ public WaveFormat RecordFormat
+ {
+ get { return recordingFormat; }
+ }
+
+ public byte[] Encode(byte[] data, int offset, int length)
+ {
+ if (offset != 0)
+ {
+ throw new ArgumentException("G722 does not yet support non-zero offsets");
+ }
+ var wb = new WaveBuffer(data);
+ int encodedLength = length / 4;
+ var outputBuffer = new byte[encodedLength];
+ int encoded = codec.Encode(encoderState, outputBuffer, wb.ShortBuffer, length / 2);
+ Debug.Assert(encodedLength == encoded);
+ return outputBuffer;
+ }
+
+ public byte[] Decode(byte[] data, int offset, int length)
+ {
+ if (offset != 0)
+ {
+ throw new ArgumentException("G722 does not yet support non-zero offsets");
+ }
+ int decodedLength = length * 4;
+ var outputBuffer = new byte[decodedLength];
+ var wb = new WaveBuffer(outputBuffer);
+ int decoded = codec.Decode(decoderState, wb.ShortBuffer, data, length);
+ Debug.Assert(decodedLength == decoded * 2); // because decoded is a number of samples
+ return outputBuffer;
+ }
+
+ public void Dispose()
+ {
+ // nothing to do
+ }
+
+ public bool IsAvailable { get { return true; } }
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/Gsm610ChatCodec.cs b/NAudioDemo/NetworkChatDemo/Gsm610ChatCodec.cs
new file mode 100644
index 00000000..eec2576b
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/Gsm610ChatCodec.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ [Export(typeof(INetworkChatCodec))]
+ class Gsm610ChatCodec : AcmChatCodec
+ {
+ public Gsm610ChatCodec()
+ : base(new WaveFormat(8000, 16, 1), new Gsm610WaveFormat())
+ {
+ }
+
+ public override string Name { get { return "GSM 6.10"; } }
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/INetworkChatCodec.cs b/NAudioDemo/NetworkChatDemo/INetworkChatCodec.cs
new file mode 100644
index 00000000..e218ee6a
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/INetworkChatCodec.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ public interface INetworkChatCodec : IDisposable
+ {
+ ///
+ /// Friendly Name for this codec
+ ///
+ string Name { get; }
+ ///
+ /// Tests whether the codec is available on this system
+ ///
+ bool IsAvailable { get; }
+ ///
+ /// Bitrate
+ ///
+ int BitsPerSecond { get; }
+ ///
+ /// Preferred PCM format for recording in (usually 8kHz mono 16 bit)
+ ///
+ WaveFormat RecordFormat { get; }
+ ///
+ /// Encodes a block of audio
+ ///
+ byte[] Encode(byte[] data, int offset, int length);
+ ///
+ /// Decodes a block of audio
+ ///
+ byte[] Decode(byte[] data, int offset, int length);
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/MicrosoftAdpcmChatCodec.cs b/NAudioDemo/NetworkChatDemo/MicrosoftAdpcmChatCodec.cs
new file mode 100644
index 00000000..cd1cb33c
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/MicrosoftAdpcmChatCodec.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ [Export(typeof(INetworkChatCodec))]
+ class MicrosoftAdpcmChatCodec : AcmChatCodec
+ {
+ public MicrosoftAdpcmChatCodec()
+ : base(new WaveFormat(8000, 16, 1), new AdpcmWaveFormat(8000,1))
+ {
+ }
+
+ public override string Name { get { return "Microsoft ADPCM"; } }
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/MuLawChatCodec.cs b/NAudioDemo/NetworkChatDemo/MuLawChatCodec.cs
new file mode 100644
index 00000000..99509f40
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/MuLawChatCodec.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using NAudio.Codecs;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ class AcmMuLawChatCodec : AcmChatCodec
+ {
+ public AcmMuLawChatCodec()
+ : base (new WaveFormat(8000,16,1), WaveFormat.CreateMuLawFormat(8000,1))
+ {
+ }
+
+ public override string Name
+ {
+ get { return "ACM G.711 mu-law"; }
+ }
+ }
+
+
+ [Export(typeof(INetworkChatCodec))]
+ class MuLawChatCodec : INetworkChatCodec
+ {
+ public string Name
+ {
+ get { return "G.711 mu-law"; }
+ }
+
+ public int BitsPerSecond
+ {
+ get { return RecordFormat.SampleRate * 8; }
+ }
+
+ public WaveFormat RecordFormat
+ {
+ get { return new WaveFormat(8000, 16, 1); }
+ }
+
+ public byte[] Encode(byte[] data, int offset, int length)
+ {
+ var encoded = new byte[length / 2];
+ int outIndex = 0;
+ for(int n = 0; n < length; n+=2)
+ {
+ encoded[outIndex++] = MuLawEncoder.LinearToMuLawSample(BitConverter.ToInt16(data, offset + n));
+ }
+ return encoded;
+ }
+
+ public byte[] Decode(byte[] data, int offset, int length)
+ {
+ var decoded = new byte[length * 2];
+ int outIndex = 0;
+ for (int n = 0; n < length; n++)
+ {
+ short decodedSample = MuLawDecoder.MuLawToLinearSample(data[n + offset]);
+ decoded[outIndex++] = (byte)(decodedSample & 0xFF);
+ decoded[outIndex++] = (byte)(decodedSample >> 8);
+ }
+ return decoded;
+ }
+
+ public void Dispose()
+ {
+ // nothing to do
+ }
+
+ public bool IsAvailable { get { return true; } }
+
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/NetworkChatPanel.Designer.cs b/NAudioDemo/NetworkChatDemo/NetworkChatPanel.Designer.cs
new file mode 100644
index 00000000..79f8e95e
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/NetworkChatPanel.Designer.cs
@@ -0,0 +1,154 @@
+namespace NAudioDemo.NetworkChatDemo
+{
+ partial class NetworkChatPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.textBoxIPAddress = new System.Windows.Forms.TextBox();
+ this.textBoxPort = new System.Windows.Forms.TextBox();
+ this.comboBoxInputDevices = new System.Windows.Forms.ComboBox();
+ this.comboBoxCodecs = new System.Windows.Forms.ComboBox();
+ this.buttonStartStreaming = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label4 = new System.Windows.Forms.Label();
+ this.SuspendLayout();
+ //
+ // textBoxIPAddress
+ //
+ this.textBoxIPAddress.Location = new System.Drawing.Point(80, 3);
+ this.textBoxIPAddress.Name = "textBoxIPAddress";
+ this.textBoxIPAddress.Size = new System.Drawing.Size(157, 20);
+ this.textBoxIPAddress.TabIndex = 0;
+ this.textBoxIPAddress.Text = "127.0.0.1";
+ //
+ // textBoxPort
+ //
+ this.textBoxPort.Location = new System.Drawing.Point(80, 29);
+ this.textBoxPort.Name = "textBoxPort";
+ this.textBoxPort.Size = new System.Drawing.Size(157, 20);
+ this.textBoxPort.TabIndex = 1;
+ this.textBoxPort.Text = "7080";
+ //
+ // comboBoxInputDevices
+ //
+ this.comboBoxInputDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxInputDevices.FormattingEnabled = true;
+ this.comboBoxInputDevices.Location = new System.Drawing.Point(80, 59);
+ this.comboBoxInputDevices.Name = "comboBoxInputDevices";
+ this.comboBoxInputDevices.Size = new System.Drawing.Size(242, 21);
+ this.comboBoxInputDevices.TabIndex = 2;
+ //
+ // comboBoxCodecs
+ //
+ this.comboBoxCodecs.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxCodecs.FormattingEnabled = true;
+ this.comboBoxCodecs.Location = new System.Drawing.Point(80, 86);
+ this.comboBoxCodecs.Name = "comboBoxCodecs";
+ this.comboBoxCodecs.Size = new System.Drawing.Size(242, 21);
+ this.comboBoxCodecs.TabIndex = 3;
+ //
+ // buttonStartStreaming
+ //
+ this.buttonStartStreaming.Location = new System.Drawing.Point(6, 125);
+ this.buttonStartStreaming.Name = "buttonStartStreaming";
+ this.buttonStartStreaming.Size = new System.Drawing.Size(129, 23);
+ this.buttonStartStreaming.TabIndex = 4;
+ this.buttonStartStreaming.Text = "Start Streaming";
+ this.buttonStartStreaming.UseVisualStyleBackColor = true;
+ this.buttonStartStreaming.Click += new System.EventHandler(this.buttonStartStreaming_Click);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(3, 5);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(61, 13);
+ this.label1.TabIndex = 6;
+ this.label1.Text = "IP Address:";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(3, 32);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(26, 13);
+ this.label2.TabIndex = 6;
+ this.label2.Text = "Port";
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(3, 62);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(71, 13);
+ this.label3.TabIndex = 7;
+ this.label3.Text = "Input Device:";
+ //
+ // label4
+ //
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(3, 89);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(67, 13);
+ this.label4.TabIndex = 8;
+ this.label4.Text = "Compression";
+ //
+ // NetworkChatPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.label4);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.buttonStartStreaming);
+ this.Controls.Add(this.comboBoxCodecs);
+ this.Controls.Add(this.comboBoxInputDevices);
+ this.Controls.Add(this.textBoxPort);
+ this.Controls.Add(this.textBoxIPAddress);
+ this.Name = "NetworkChatPanel";
+ this.Size = new System.Drawing.Size(593, 228);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TextBox textBoxIPAddress;
+ private System.Windows.Forms.TextBox textBoxPort;
+ private System.Windows.Forms.ComboBox comboBoxInputDevices;
+ private System.Windows.Forms.ComboBox comboBoxCodecs;
+ private System.Windows.Forms.Button buttonStartStreaming;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label label4;
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/NetworkChatPanel.cs b/NAudioDemo/NetworkChatDemo/NetworkChatPanel.cs
new file mode 100644
index 00000000..486ec92e
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/NetworkChatPanel.cs
@@ -0,0 +1,189 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+using NAudio.Wave;
+using System.Net.Sockets;
+using System.Threading;
+using System.Net;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ public partial class NetworkChatPanel : UserControl
+ {
+ private WaveIn waveIn;
+ private UdpClient udpSender;
+ private UdpClient udpListener;
+ private IWavePlayer waveOut;
+ private BufferedWaveProvider waveProvider;
+ private INetworkChatCodec selectedCodec;
+ private volatile bool connected;
+
+ public NetworkChatPanel(IEnumerable codecs)
+ {
+ InitializeComponent();
+ PopulateInputDevicesCombo();
+ PopulateCodecsCombo(codecs);
+ Disposed += OnPanelDisposed;
+ }
+
+ void OnPanelDisposed(object sender, EventArgs e)
+ {
+ Disconnect();
+ }
+
+ private void PopulateCodecsCombo(IEnumerable codecs)
+ {
+ var sorted = from codec in codecs
+ where codec.IsAvailable
+ orderby codec.BitsPerSecond ascending
+ select codec;
+
+ foreach(var codec in sorted)
+ {
+ string bitRate = codec.BitsPerSecond == -1 ? "VBR" : String.Format("{0:0.#}kbps", codec.BitsPerSecond / 1000.0);
+ string text = String.Format("{0} ({1})", codec.Name, bitRate);
+ comboBoxCodecs.Items.Add(new CodecComboItem { Text=text, Codec=codec });
+ }
+ comboBoxCodecs.SelectedIndex = 0;
+ }
+
+ class CodecComboItem
+ {
+ public string Text { get; set; }
+ public INetworkChatCodec Codec { get; set; }
+ public override string ToString()
+ {
+ return Text;
+ }
+ }
+
+ private void PopulateInputDevicesCombo()
+ {
+ for (int n = 0; n < WaveIn.DeviceCount; n++)
+ {
+ var capabilities = WaveIn.GetCapabilities(n);
+ comboBoxInputDevices.Items.Add(capabilities.ProductName);
+ }
+ if (comboBoxInputDevices.Items.Count > 0)
+ {
+ comboBoxInputDevices.SelectedIndex = 0;
+ }
+ }
+
+ private void buttonStartStreaming_Click(object sender, EventArgs e)
+ {
+ if (!connected)
+ {
+ IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(textBoxIPAddress.Text), int.Parse(textBoxPort.Text));
+ int inputDeviceNumber = comboBoxInputDevices.SelectedIndex;
+ selectedCodec = ((CodecComboItem)comboBoxCodecs.SelectedItem).Codec;
+ Connect(endPoint, inputDeviceNumber,selectedCodec);
+ buttonStartStreaming.Text = "Disconnect";
+ }
+ else
+ {
+ Disconnect();
+ buttonStartStreaming.Text = "Connect";
+ }
+ }
+
+ private void Connect(IPEndPoint endPoint, int inputDeviceNumber, INetworkChatCodec codec)
+ {
+ waveIn = new WaveIn();
+ waveIn.BufferMilliseconds = 50;
+ waveIn.DeviceNumber = inputDeviceNumber;
+ waveIn.WaveFormat = codec.RecordFormat;
+ waveIn.DataAvailable += waveIn_DataAvailable;
+ waveIn.StartRecording();
+
+ udpSender = new UdpClient();
+ udpListener = new UdpClient();
+
+ // To allow us to talk to ourselves for test purposes:
+ // http://stackoverflow.com/questions/687868/sending-and-receiving-udp-packets-between-two-programs-on-the-same-computer
+ udpListener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ udpListener.Client.Bind(endPoint);
+
+ udpSender.Connect(endPoint);
+
+ waveOut = new WaveOut();
+ waveProvider = new BufferedWaveProvider(codec.RecordFormat);
+ waveOut.Init(waveProvider);
+ waveOut.Play();
+
+ connected = true;
+ var state = new ListenerThreadState { Codec = codec, EndPoint = endPoint };
+ ThreadPool.QueueUserWorkItem(ListenerThread, state);
+ }
+
+ private void Disconnect()
+ {
+ if (connected)
+ {
+ connected = false;
+ waveIn.DataAvailable -= waveIn_DataAvailable;
+ waveIn.StopRecording();
+ waveOut.Stop();
+
+ udpSender.Close();
+ udpListener.Close();
+ waveIn.Dispose();
+ waveOut.Dispose();
+
+ // a bit naughty but we have designed the codecs to support multiple calls to Dispose,
+ // recreating their resources if Encode/Decode called again
+ selectedCodec.Dispose();
+ }
+ }
+
+ class ListenerThreadState
+ {
+ public IPEndPoint EndPoint { get; set; }
+ public INetworkChatCodec Codec { get; set; }
+ }
+
+ private void ListenerThread(object state)
+ {
+ var listenerThreadState = (ListenerThreadState)state;
+ var endPoint = listenerThreadState.EndPoint;
+ try
+ {
+ while (connected)
+ {
+ byte[] b = udpListener.Receive(ref endPoint);
+ byte[] decoded = listenerThreadState.Codec.Decode(b, 0, b.Length);
+ waveProvider.AddSamples(decoded, 0, decoded.Length);
+ }
+ }
+ catch (SocketException)
+ {
+ // usually not a problem - just means we have disconnected
+ }
+ }
+
+ void waveIn_DataAvailable(object sender, WaveInEventArgs e)
+ {
+ byte[] encoded = selectedCodec.Encode(e.Buffer, 0, e.BytesRecorded);
+ udpSender.Send(encoded, encoded.Length);
+ }
+ }
+
+ [Export(typeof(INAudioDemoPlugin))]
+ public class NetworkChatPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "Network Chat"; }
+ }
+
+ [ImportMany(typeof(INetworkChatCodec))]
+ public IEnumerable Codecs { get; set; }
+
+ public Control CreatePanel()
+ {
+ return new NetworkChatPanel(Codecs);
+ }
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/NetworkChatPanel.resx b/NAudioDemo/NetworkChatDemo/NetworkChatPanel.resx
new file mode 100644
index 00000000..5ea0895e
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/NetworkChatPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/NetworkChatDemo/SpeexChatCodec.cs b/NAudioDemo/NetworkChatDemo/SpeexChatCodec.cs
new file mode 100644
index 00000000..8891393d
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/SpeexChatCodec.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using NSpeex;
+using System.ComponentModel.Composition;
+using System.Diagnostics;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ [Export(typeof(INetworkChatCodec))]
+ class NarrowBandSpeexCodec : SpeexChatCodec
+ {
+ public NarrowBandSpeexCodec() :
+ base(BandMode.Narrow, 8000, "Speex Narrow Band")
+ {
+
+ }
+ }
+
+ [Export(typeof(INetworkChatCodec))]
+ class WideBandSpeexCodec : SpeexChatCodec
+ {
+ public WideBandSpeexCodec() :
+ base(BandMode.Wide, 16000, "Speex Wide Band (16kHz)")
+ {
+
+ }
+ }
+
+ [Export(typeof(INetworkChatCodec))]
+ class UltraWideBandSpeexCodec : SpeexChatCodec
+ {
+ public UltraWideBandSpeexCodec() :
+ base(BandMode.UltraWide, 32000, "Speex Ultra Wide Band (32kHz)")
+ {
+
+ }
+ }
+
+ class SpeexChatCodec : INetworkChatCodec
+ {
+ private readonly WaveFormat recordingFormat;
+ private readonly SpeexDecoder decoder;
+ private readonly SpeexEncoder encoder;
+ private readonly WaveBuffer encoderInputBuffer;
+ private readonly string description;
+
+ public SpeexChatCodec(BandMode bandMode, int sampleRate, string description)
+ {
+ decoder = new SpeexDecoder(bandMode);
+ encoder = new SpeexEncoder(bandMode);
+ recordingFormat = new WaveFormat(sampleRate, 16, 1);
+ this.description = description;
+ encoderInputBuffer = new WaveBuffer(recordingFormat.AverageBytesPerSecond); // more than enough
+ }
+
+ public string Name
+ {
+ get { return description; }
+ }
+
+ public int BitsPerSecond
+ {
+ get { return -1; }
+ }
+
+ public WaveFormat RecordFormat
+ {
+ get { return recordingFormat; }
+ }
+
+ public byte[] Encode(byte[] data, int offset, int length)
+ {
+ FeedSamplesIntoEncoderInputBuffer(data, offset, length);
+ int samplesToEncode = encoderInputBuffer.ShortBufferCount;
+ if (samplesToEncode % encoder.FrameSize != 0)
+ {
+ samplesToEncode -= samplesToEncode % encoder.FrameSize;
+ }
+ var outputBufferTemp = new byte[length]; // contains more than enough space
+ int bytesWritten = encoder.Encode(encoderInputBuffer.ShortBuffer, 0, samplesToEncode, outputBufferTemp, 0, length);
+ var encoded = new byte[bytesWritten];
+ Array.Copy(outputBufferTemp, 0, encoded, 0, bytesWritten);
+ ShiftLeftoverSamplesDown(samplesToEncode);
+ Debug.WriteLine(String.Format("NSpeex: In {0} bytes, encoded {1} bytes [enc frame size = {2}]",length,bytesWritten, encoder.FrameSize));
+ return encoded;
+ }
+
+ private void ShiftLeftoverSamplesDown(int samplesEncoded)
+ {
+ int leftoverSamples = encoderInputBuffer.ShortBufferCount - samplesEncoded;
+ Array.Copy(encoderInputBuffer.ByteBuffer, samplesEncoded * 2, encoderInputBuffer.ByteBuffer, 0, leftoverSamples * 2);
+ encoderInputBuffer.ShortBufferCount = leftoverSamples;
+ }
+
+ private void FeedSamplesIntoEncoderInputBuffer(byte[] data, int offset, int length)
+ {
+ Array.Copy(data, offset, encoderInputBuffer.ByteBuffer, encoderInputBuffer.ByteBufferCount, length);
+ encoderInputBuffer.ByteBufferCount += length;
+ }
+
+ public byte[] Decode(byte[] data, int offset, int length)
+ {
+ var outputBufferTemp = new byte[length * 320];
+ var wb = new WaveBuffer(outputBufferTemp);
+ int samplesDecoded = decoder.Decode(data, offset, length, wb.ShortBuffer, 0, false);
+ int bytesDecoded = samplesDecoded * 2;
+ var decoded = new byte[bytesDecoded];
+ Array.Copy(outputBufferTemp, 0, decoded, 0, bytesDecoded);
+ Debug.WriteLine(String.Format("NSpeex: In {0} bytes, decoded {1} bytes [dec frame size = {2}]", length, bytesDecoded, decoder.FrameSize));
+ return decoded;
+ }
+
+ public void Dispose()
+ {
+ // nothing to do
+ }
+
+ public bool IsAvailable { get { return true; } }
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/TrueSpeechChatCodec.cs b/NAudioDemo/NetworkChatDemo/TrueSpeechChatCodec.cs
new file mode 100644
index 00000000..0add1389
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/TrueSpeechChatCodec.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ ///
+ /// DSP Group TrueSpeech codec, using ACM
+ /// n.b. Windows XP came with a TrueSpeech codec built in
+ /// - looks like Windows 7 doesn't
+ ///
+ [Export(typeof(INetworkChatCodec))]
+ class TrueSpeechChatCodec : AcmChatCodec
+ {
+ public TrueSpeechChatCodec()
+ : base(new WaveFormat(8000, 16, 1), new TrueSpeechWaveFormat())
+ {
+ }
+
+
+ public override string Name
+ {
+ get { return "DSP Group TrueSpeech"; }
+ }
+
+ }
+}
diff --git a/NAudioDemo/NetworkChatDemo/UncompressedPcmChatCodec.cs b/NAudioDemo/NetworkChatDemo/UncompressedPcmChatCodec.cs
new file mode 100644
index 00000000..541f3f0a
--- /dev/null
+++ b/NAudioDemo/NetworkChatDemo/UncompressedPcmChatCodec.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Linq;
+using NAudio.Wave;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo.NetworkChatDemo
+{
+ [Export(typeof(INetworkChatCodec))]
+ class UncompressedPcmChatCodec : INetworkChatCodec
+ {
+ public UncompressedPcmChatCodec()
+ {
+ RecordFormat = new WaveFormat(8000, 16, 1);
+ }
+
+ public string Name { get { return "PCM 8kHz 16 bit uncompressed"; } }
+
+ public WaveFormat RecordFormat { get; private set; }
+
+ public byte[] Encode(byte[] data, int offset, int length)
+ {
+ var encoded = new byte[length];
+ Array.Copy(data, offset, encoded, 0, length);
+ return encoded;
+ }
+
+ public byte[] Decode(byte[] data, int offset, int length)
+ {
+ var decoded = new byte[length];
+ Array.Copy(data, offset, decoded, 0, length);
+ return decoded;
+ }
+
+ public int BitsPerSecond { get { return RecordFormat.AverageBytesPerSecond * 8; } }
+
+ public void Dispose() { }
+
+ public bool IsAvailable { get { return true; } }
+ }
+}
diff --git a/NAudioDemo/Program.cs b/NAudioDemo/Program.cs
new file mode 100644
index 00000000..15321947
--- /dev/null
+++ b/NAudioDemo/Program.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using System.ComponentModel.Composition.Hosting;
+using Microsoft.ComponentModel.Composition.Hosting;
+
+namespace NAudioDemo
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ //[MTAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+
+ var catalog = new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly());
+ var exportFactoryProvider = new ExportFactoryProvider(); // enable use of ExportFactory
+ var container = new CompositionContainer(catalog, exportFactoryProvider);
+ exportFactoryProvider.SourceProvider = container; // enable use of ExportFactory
+ var mainForm = container.GetExportedValue();
+ Application.Run(mainForm);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/Properties/AssemblyInfo.cs b/NAudioDemo/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..b7d1c787
--- /dev/null
+++ b/NAudioDemo/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NAudioDemo")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NAudioDemo")]
+[assembly: AssemblyCopyright("Copyright © 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("6f558ff4-35cc-4cdc-8767-ca7f3b679f13")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/NAudioDemo/Properties/Resources.Designer.cs b/NAudioDemo/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..124fec05
--- /dev/null
+++ b/NAudioDemo/Properties/Resources.Designer.cs
@@ -0,0 +1,63 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.225
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace NAudioDemo.Properties {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NAudioDemo.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/NAudioDemo/Properties/Resources.resx b/NAudioDemo/Properties/Resources.resx
new file mode 100644
index 00000000..c40a448a
--- /dev/null
+++ b/NAudioDemo/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/Properties/Settings.Designer.cs b/NAudioDemo/Properties/Settings.Designer.cs
new file mode 100644
index 00000000..9b3b727e
--- /dev/null
+++ b/NAudioDemo/Properties/Settings.Designer.cs
@@ -0,0 +1,38 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.225
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace NAudioDemo.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string AudioFolder {
+ get {
+ return ((string)(this["AudioFolder"]));
+ }
+ set {
+ this["AudioFolder"] = value;
+ }
+ }
+ }
+}
diff --git a/NAudioDemo/Properties/Settings.settings b/NAudioDemo/Properties/Settings.settings
new file mode 100644
index 00000000..07e640b7
--- /dev/null
+++ b/NAudioDemo/Properties/Settings.settings
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudioDemo/RecordingDemo/RecordingPanel.Designer.cs b/NAudioDemo/RecordingDemo/RecordingPanel.Designer.cs
new file mode 100644
index 00000000..044ec03a
--- /dev/null
+++ b/NAudioDemo/RecordingDemo/RecordingPanel.Designer.cs
@@ -0,0 +1,246 @@
+namespace NAudioDemo
+{
+ partial class RecordingPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(RecordingPanel));
+ this.buttonStartRecording = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ this.buttonStopRecording = new System.Windows.Forms.Button();
+ this.progressBar1 = new System.Windows.Forms.ProgressBar();
+ this.label2 = new System.Windows.Forms.Label();
+ this.listBoxRecordings = new System.Windows.Forms.ListBox();
+ this.buttonPlay = new System.Windows.Forms.Button();
+ this.buttonDelete = new System.Windows.Forms.Button();
+ this.buttonOpenFolder = new System.Windows.Forms.Button();
+ this.groupBoxRecordingApi = new System.Windows.Forms.GroupBox();
+ this.comboWasapiDevices = new System.Windows.Forms.ComboBox();
+ this.radioButtonWasapiLoopback = new System.Windows.Forms.RadioButton();
+ this.radioButtonWasapi = new System.Windows.Forms.RadioButton();
+ this.radioButtonWaveInEvent = new System.Windows.Forms.RadioButton();
+ this.radioButtonWaveIn = new System.Windows.Forms.RadioButton();
+ this.groupBoxRecordingApi.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // buttonStartRecording
+ //
+ this.buttonStartRecording.Location = new System.Drawing.Point(313, 66);
+ this.buttonStartRecording.Name = "buttonStartRecording";
+ this.buttonStartRecording.Size = new System.Drawing.Size(105, 23);
+ this.buttonStartRecording.TabIndex = 0;
+ this.buttonStartRecording.Text = "Start Recording";
+ this.buttonStartRecording.UseVisualStyleBackColor = true;
+ this.buttonStartRecording.Click += new System.EventHandler(this.OnButtonStartRecordingClick);
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.label1.Location = new System.Drawing.Point(12, 16);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(576, 44);
+ this.label1.TabIndex = 1;
+ this.label1.Text = resources.GetString("label1.Text");
+ //
+ // buttonStopRecording
+ //
+ this.buttonStopRecording.Location = new System.Drawing.Point(435, 66);
+ this.buttonStopRecording.Name = "buttonStopRecording";
+ this.buttonStopRecording.Size = new System.Drawing.Size(105, 23);
+ this.buttonStopRecording.TabIndex = 0;
+ this.buttonStopRecording.Text = "Stop Recording";
+ this.buttonStopRecording.UseVisualStyleBackColor = true;
+ this.buttonStopRecording.Click += new System.EventHandler(this.OnButtonStopRecordingClick);
+ //
+ // progressBar1
+ //
+ this.progressBar1.Location = new System.Drawing.Point(313, 125);
+ this.progressBar1.Maximum = 30;
+ this.progressBar1.Name = "progressBar1";
+ this.progressBar1.Size = new System.Drawing.Size(257, 23);
+ this.progressBar1.TabIndex = 4;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(310, 106);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(103, 13);
+ this.label2.TabIndex = 5;
+ this.label2.Text = "Recording Progress:";
+ //
+ // listBoxRecordings
+ //
+ this.listBoxRecordings.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.listBoxRecordings.FormattingEnabled = true;
+ this.listBoxRecordings.Location = new System.Drawing.Point(15, 198);
+ this.listBoxRecordings.Name = "listBoxRecordings";
+ this.listBoxRecordings.Size = new System.Drawing.Size(475, 108);
+ this.listBoxRecordings.TabIndex = 8;
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonPlay.Location = new System.Drawing.Point(496, 211);
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(75, 23);
+ this.buttonPlay.TabIndex = 9;
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.UseVisualStyleBackColor = true;
+ this.buttonPlay.Click += new System.EventHandler(this.OnButtonPlayClick);
+ //
+ // buttonDelete
+ //
+ this.buttonDelete.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonDelete.Location = new System.Drawing.Point(496, 240);
+ this.buttonDelete.Name = "buttonDelete";
+ this.buttonDelete.Size = new System.Drawing.Size(75, 23);
+ this.buttonDelete.TabIndex = 10;
+ this.buttonDelete.Text = "Delete";
+ this.buttonDelete.UseVisualStyleBackColor = true;
+ this.buttonDelete.Click += new System.EventHandler(this.OnButtonDeleteClick);
+ //
+ // buttonOpenFolder
+ //
+ this.buttonOpenFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.buttonOpenFolder.Location = new System.Drawing.Point(496, 269);
+ this.buttonOpenFolder.Name = "buttonOpenFolder";
+ this.buttonOpenFolder.Size = new System.Drawing.Size(75, 23);
+ this.buttonOpenFolder.TabIndex = 10;
+ this.buttonOpenFolder.Text = "Open Folder";
+ this.buttonOpenFolder.UseVisualStyleBackColor = true;
+ this.buttonOpenFolder.Click += new System.EventHandler(this.OnOpenFolderClick);
+ //
+ // groupBoxRecordingApi
+ //
+ this.groupBoxRecordingApi.Controls.Add(this.comboWasapiDevices);
+ this.groupBoxRecordingApi.Controls.Add(this.radioButtonWasapiLoopback);
+ this.groupBoxRecordingApi.Controls.Add(this.radioButtonWasapi);
+ this.groupBoxRecordingApi.Controls.Add(this.radioButtonWaveInEvent);
+ this.groupBoxRecordingApi.Controls.Add(this.radioButtonWaveIn);
+ this.groupBoxRecordingApi.Location = new System.Drawing.Point(15, 66);
+ this.groupBoxRecordingApi.Name = "groupBoxRecordingApi";
+ this.groupBoxRecordingApi.Size = new System.Drawing.Size(269, 112);
+ this.groupBoxRecordingApi.TabIndex = 11;
+ this.groupBoxRecordingApi.TabStop = false;
+ this.groupBoxRecordingApi.Text = "Recording API";
+ //
+ // comboWasapiDevices
+ //
+ this.comboWasapiDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboWasapiDevices.FormattingEnabled = true;
+ this.comboWasapiDevices.Location = new System.Drawing.Point(141, 59);
+ this.comboWasapiDevices.Name = "comboWasapiDevices";
+ this.comboWasapiDevices.Size = new System.Drawing.Size(121, 21);
+ this.comboWasapiDevices.TabIndex = 12;
+ //
+ // radioButtonWasapiLoopback
+ //
+ this.radioButtonWasapiLoopback.AutoSize = true;
+ this.radioButtonWasapiLoopback.Location = new System.Drawing.Point(6, 86);
+ this.radioButtonWasapiLoopback.Name = "radioButtonWasapiLoopback";
+ this.radioButtonWasapiLoopback.Size = new System.Drawing.Size(118, 17);
+ this.radioButtonWasapiLoopback.TabIndex = 8;
+ this.radioButtonWasapiLoopback.Text = "WASAPI Loopback";
+ this.radioButtonWasapiLoopback.UseVisualStyleBackColor = true;
+ //
+ // radioButtonWasapi
+ //
+ this.radioButtonWasapi.AutoSize = true;
+ this.radioButtonWasapi.Location = new System.Drawing.Point(6, 63);
+ this.radioButtonWasapi.Name = "radioButtonWasapi";
+ this.radioButtonWasapi.Size = new System.Drawing.Size(67, 17);
+ this.radioButtonWasapi.TabIndex = 9;
+ this.radioButtonWasapi.Text = "WASAPI";
+ this.radioButtonWasapi.UseVisualStyleBackColor = true;
+ //
+ // radioButtonWaveInEvent
+ //
+ this.radioButtonWaveInEvent.AutoSize = true;
+ this.radioButtonWaveInEvent.Location = new System.Drawing.Point(6, 40);
+ this.radioButtonWaveInEvent.Name = "radioButtonWaveInEvent";
+ this.radioButtonWaveInEvent.Size = new System.Drawing.Size(140, 17);
+ this.radioButtonWaveInEvent.TabIndex = 10;
+ this.radioButtonWaveInEvent.Text = "waveIn Event Callbacks";
+ this.radioButtonWaveInEvent.UseVisualStyleBackColor = true;
+ //
+ // radioButtonWaveIn
+ //
+ this.radioButtonWaveIn.AutoSize = true;
+ this.radioButtonWaveIn.Checked = true;
+ this.radioButtonWaveIn.Location = new System.Drawing.Point(6, 17);
+ this.radioButtonWaveIn.Name = "radioButtonWaveIn";
+ this.radioButtonWaveIn.Size = new System.Drawing.Size(60, 17);
+ this.radioButtonWaveIn.TabIndex = 11;
+ this.radioButtonWaveIn.TabStop = true;
+ this.radioButtonWaveIn.Text = "waveIn";
+ this.radioButtonWaveIn.UseVisualStyleBackColor = true;
+ //
+ // RecordingPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.groupBoxRecordingApi);
+ this.Controls.Add(this.buttonOpenFolder);
+ this.Controls.Add(this.buttonDelete);
+ this.Controls.Add(this.buttonPlay);
+ this.Controls.Add(this.listBoxRecordings);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.progressBar1);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.buttonStopRecording);
+ this.Controls.Add(this.buttonStartRecording);
+ this.Name = "RecordingPanel";
+ this.Size = new System.Drawing.Size(600, 321);
+ this.groupBoxRecordingApi.ResumeLayout(false);
+ this.groupBoxRecordingApi.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button buttonStartRecording;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button buttonStopRecording;
+ private System.Windows.Forms.ProgressBar progressBar1;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.ListBox listBoxRecordings;
+ private System.Windows.Forms.Button buttonPlay;
+ private System.Windows.Forms.Button buttonDelete;
+ private System.Windows.Forms.Button buttonOpenFolder;
+ private System.Windows.Forms.GroupBox groupBoxRecordingApi;
+ private System.Windows.Forms.ComboBox comboWasapiDevices;
+ private System.Windows.Forms.RadioButton radioButtonWasapiLoopback;
+ private System.Windows.Forms.RadioButton radioButtonWasapi;
+ private System.Windows.Forms.RadioButton radioButtonWaveInEvent;
+ private System.Windows.Forms.RadioButton radioButtonWaveIn;
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/RecordingDemo/RecordingPanel.cs b/NAudioDemo/RecordingDemo/RecordingPanel.cs
new file mode 100644
index 00000000..e180855c
--- /dev/null
+++ b/NAudioDemo/RecordingDemo/RecordingPanel.cs
@@ -0,0 +1,228 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Windows.Forms;
+using NAudio.Wave;
+using System.Diagnostics;
+using NAudio.CoreAudioApi;
+using System.ComponentModel.Composition;
+
+namespace NAudioDemo
+{
+ public partial class RecordingPanel : UserControl
+ {
+ private IWaveIn waveIn;
+ private WaveFileWriter writer;
+ private string outputFilename;
+ private readonly string outputFolder;
+
+ public RecordingPanel()
+ {
+ InitializeComponent();
+ Disposed += OnRecordingPanelDisposed;
+ if (Environment.OSVersion.Version.Major >= 6)
+ {
+ LoadWasapiDevicesCombo();
+ }
+ else
+ {
+ radioButtonWasapi.Enabled = false;
+ comboWasapiDevices.Enabled = false;
+ radioButtonWasapiLoopback.Enabled = false;
+ }
+ outputFolder = Path.Combine(Path.GetTempPath(), "NAudioDemo");
+ Directory.CreateDirectory(outputFolder);
+
+ // close the device if we change option only
+ radioButtonWasapi.CheckedChanged += (s, a) => Cleanup();
+ radioButtonWaveIn.CheckedChanged += (s, a) => Cleanup();
+ radioButtonWaveInEvent.CheckedChanged += (s, a) => Cleanup();
+ radioButtonWasapiLoopback.CheckedChanged += (s, a) => Cleanup();
+ }
+
+ void OnRecordingPanelDisposed(object sender, EventArgs e)
+ {
+ Cleanup();
+ }
+
+ private void LoadWasapiDevicesCombo()
+ {
+ var deviceEnum = new MMDeviceEnumerator();
+ var devices = deviceEnum.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.Active).ToList();
+
+ comboWasapiDevices.DataSource = devices;
+ comboWasapiDevices.DisplayMember = "FriendlyName";
+ }
+
+ private void OnButtonStartRecordingClick(object sender, EventArgs e)
+ {
+ if (radioButtonWaveIn.Checked)
+ Cleanup(); // WaveIn is still unreliable in some circumstances to being reused
+
+ if (waveIn == null)
+ {
+ CreateWaveInDevice();
+ }
+
+ outputFilename = String.Format("NAudioDemo {0:yyy-MM-dd HH-mm-ss}.wav", DateTime.Now);
+ writer = new WaveFileWriter(Path.Combine(outputFolder, outputFilename), waveIn.WaveFormat);
+ waveIn.StartRecording();
+ SetControlStates(true);
+ }
+
+ private void CreateWaveInDevice()
+ {
+ if (radioButtonWaveIn.Checked)
+ {
+ waveIn = new WaveIn();
+ waveIn.WaveFormat = new WaveFormat(8000, 1);
+ }
+ else if (radioButtonWaveInEvent.Checked)
+ {
+ waveIn = new WaveInEvent();
+ waveIn.WaveFormat = new WaveFormat(8000, 1);
+ }
+ else if (radioButtonWasapi.Checked)
+ {
+ // can't set WaveFormat as WASAPI doesn't support SRC
+ var device = (MMDevice) comboWasapiDevices.SelectedItem;
+ waveIn = new WasapiCapture(device);
+ }
+ else
+ {
+ // can't set WaveFormat as WASAPI doesn't support SRC
+ waveIn = new WasapiLoopbackCapture();
+ }
+ waveIn.DataAvailable += OnDataAvailable;
+ waveIn.RecordingStopped += OnRecordingStopped;
+ }
+
+ void OnRecordingStopped(object sender, StoppedEventArgs e)
+ {
+ if (InvokeRequired)
+ {
+ BeginInvoke(new EventHandler(OnRecordingStopped), sender, e);
+ }
+ else
+ {
+ FinalizeWaveFile();
+ progressBar1.Value = 0;
+ if (e.Exception != null)
+ {
+ MessageBox.Show(String.Format("A problem was encountered during recording {0}",
+ e.Exception.Message));
+ }
+ int newItemIndex = listBoxRecordings.Items.Add(outputFilename);
+ listBoxRecordings.SelectedIndex = newItemIndex;
+ SetControlStates(false);
+ }
+ }
+
+ private void Cleanup()
+ {
+ if (waveIn != null)
+ {
+ waveIn.Dispose();
+ waveIn = null;
+ }
+ FinalizeWaveFile();
+ }
+
+ private void FinalizeWaveFile()
+ {
+ if (writer != null)
+ {
+ writer.Dispose();
+ writer = null;
+ }
+ }
+
+ void OnDataAvailable(object sender, WaveInEventArgs e)
+ {
+ if (this.InvokeRequired)
+ {
+ //Debug.WriteLine("Data Available");
+ this.BeginInvoke(new EventHandler(OnDataAvailable), sender, e);
+ }
+ else
+ {
+ //Debug.WriteLine("Flushing Data Available");
+ writer.Write(e.Buffer, 0, e.BytesRecorded);
+ int secondsRecorded = (int)(writer.Length / writer.WaveFormat.AverageBytesPerSecond);
+ if (secondsRecorded >= 30)
+ {
+ StopRecording();
+ }
+ else
+ {
+ progressBar1.Value = secondsRecorded;
+ }
+ }
+ }
+
+ void StopRecording()
+ {
+ Debug.WriteLine("StopRecording");
+ if (waveIn != null) waveIn.StopRecording();
+ }
+
+ private void OnButtonStopRecordingClick(object sender, EventArgs e)
+ {
+ StopRecording();
+ }
+
+ private void OnButtonPlayClick(object sender, EventArgs e)
+ {
+ if (listBoxRecordings.SelectedItem != null)
+ {
+ Process.Start(Path.Combine(outputFolder, (string)listBoxRecordings.SelectedItem));
+ }
+ }
+
+ private void SetControlStates(bool isRecording)
+ {
+ groupBoxRecordingApi.Enabled = !isRecording;
+ buttonStartRecording.Enabled = !isRecording;
+ buttonStopRecording.Enabled = isRecording;
+ }
+
+ private void OnButtonDeleteClick(object sender, EventArgs e)
+ {
+ if (listBoxRecordings.SelectedItem != null)
+ {
+ try
+ {
+ File.Delete(Path.Combine(outputFolder, (string)listBoxRecordings.SelectedItem));
+ listBoxRecordings.Items.Remove(listBoxRecordings.SelectedItem);
+ if (listBoxRecordings.Items.Count > 0)
+ {
+ listBoxRecordings.SelectedIndex = 0;
+ }
+ }
+ catch (Exception)
+ {
+ MessageBox.Show("Could not delete recording");
+ }
+ }
+ }
+
+ private void OnOpenFolderClick(object sender, EventArgs e)
+ {
+ Process.Start(outputFolder);
+ }
+ }
+
+ [Export(typeof(INAudioDemoPlugin))]
+ public class RecordingPanelPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "WAV Recording"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new RecordingPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/RecordingDemo/RecordingPanel.resx b/NAudioDemo/RecordingDemo/RecordingPanel.resx
new file mode 100644
index 00000000..04bd4146
--- /dev/null
+++ b/NAudioDemo/RecordingDemo/RecordingPanel.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ This basic recording demo will open the default input device and start recording audio at 8kHz 16 bit (for WaveIn, WASAPI will use its default format). It will automatically stop recording after 30 seconds after which you can optionally play back the recorded file.
+
+
\ No newline at end of file
diff --git a/NAudioDemo/Resources/Back.png b/NAudioDemo/Resources/Back.png
new file mode 100644
index 00000000..357e645c
Binary files /dev/null and b/NAudioDemo/Resources/Back.png differ
diff --git a/NAudioDemo/Resources/Forward.png b/NAudioDemo/Resources/Forward.png
new file mode 100644
index 00000000..43e95404
Binary files /dev/null and b/NAudioDemo/Resources/Forward.png differ
diff --git a/NAudioDemo/Resources/Loop.png b/NAudioDemo/Resources/Loop.png
new file mode 100644
index 00000000..be08c0ec
Binary files /dev/null and b/NAudioDemo/Resources/Loop.png differ
diff --git a/NAudioDemo/Resources/Open.png b/NAudioDemo/Resources/Open.png
new file mode 100644
index 00000000..0bc670e2
Binary files /dev/null and b/NAudioDemo/Resources/Open.png differ
diff --git a/NAudioDemo/Resources/Pause.png b/NAudioDemo/Resources/Pause.png
new file mode 100644
index 00000000..189a5453
Binary files /dev/null and b/NAudioDemo/Resources/Pause.png differ
diff --git a/NAudioDemo/Resources/Play.png b/NAudioDemo/Resources/Play.png
new file mode 100644
index 00000000..93bc2af1
Binary files /dev/null and b/NAudioDemo/Resources/Play.png differ
diff --git a/NAudioDemo/Resources/Rewind.png b/NAudioDemo/Resources/Rewind.png
new file mode 100644
index 00000000..c8feb415
Binary files /dev/null and b/NAudioDemo/Resources/Rewind.png differ
diff --git a/NAudioDemo/Resources/Shuffle.png b/NAudioDemo/Resources/Shuffle.png
new file mode 100644
index 00000000..243ed82e
Binary files /dev/null and b/NAudioDemo/Resources/Shuffle.png differ
diff --git a/NAudioDemo/Resources/Stop.png b/NAudioDemo/Resources/Stop.png
new file mode 100644
index 00000000..49038201
Binary files /dev/null and b/NAudioDemo/Resources/Stop.png differ
diff --git a/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.Designer.cs b/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.Designer.cs
new file mode 100644
index 00000000..af98a063
--- /dev/null
+++ b/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.Designer.cs
@@ -0,0 +1,585 @@
+namespace NAudioDemo.Generator
+{
+ partial class GeneratorPanel
+ {
+ ///
+ /// Variable nécessaire au concepteur.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Nettoyage des ressources utilisées.
+ ///
+ /// true si les ressources managées doivent être supprimées ; sinon, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Code généré par le Concepteur de composants
+
+ ///
+ /// Méthode requise pour la prise en charge du concepteur - ne modifiez pas
+ /// le contenu de cette méthode avec l'éditeur de code.
+ ///
+ private void InitializeComponent()
+ {
+ this.btnStart = new System.Windows.Forms.Button();
+ this.btnStop = new System.Windows.Forms.Button();
+ this.cmbType = new System.Windows.Forms.ComboBox();
+ this.label1 = new System.Windows.Forms.Label();
+ this.tbFrq = new System.Windows.Forms.TrackBar();
+ this.lblFrq = new System.Windows.Forms.Label();
+ this.lblFrqTitle = new System.Windows.Forms.Label();
+ this.lblFrqEndTitle = new System.Windows.Forms.Label();
+ this.tbFrqEnd = new System.Windows.Forms.TrackBar();
+ this.lblFrqEnd = new System.Windows.Forms.Label();
+ this.lblFrqEndUnit = new System.Windows.Forms.Label();
+ this.tbGain = new System.Windows.Forms.TrackBar();
+ this.lblGain = new System.Windows.Forms.Label();
+ this.label8 = new System.Windows.Forms.Label();
+ this.tbSweepLength = new System.Windows.Forms.TrackBar();
+ this.lblSweepLength = new System.Windows.Forms.Label();
+ this.lblSweepLengthUnit = new System.Windows.Forms.Label();
+ this.chkReverseLeft = new System.Windows.Forms.CheckBox();
+ this.chkReverseRight = new System.Windows.Forms.CheckBox();
+ this.cmbPrecisionFrq = new System.Windows.Forms.ComboBox();
+ this.lblFrqPrecision = new System.Windows.Forms.Label();
+ this.cmbPrecisionFrqEnd = new System.Windows.Forms.ComboBox();
+ this.lblFrqEndPrecision = new System.Windows.Forms.Label();
+ this.lblFrqUnit = new System.Windows.Forms.Label();
+ this.cmbFrq = new System.Windows.Forms.ComboBox();
+ this.grbFrq = new System.Windows.Forms.GroupBox();
+ this.lblPreset = new System.Windows.Forms.Label();
+ this.grpFrqEnd = new System.Windows.Forms.GroupBox();
+ this.cmbFrqEnd = new System.Windows.Forms.ComboBox();
+ this.lblEndPreset = new System.Windows.Forms.Label();
+ this.groupBox1 = new System.Windows.Forms.GroupBox();
+ this.grpSweepLength = new System.Windows.Forms.GroupBox();
+ this.grpPhaseReverse = new System.Windows.Forms.GroupBox();
+ this.buttonSave = new System.Windows.Forms.Button();
+ ((System.ComponentModel.ISupportInitialize)(this.tbFrq)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.tbFrqEnd)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.tbGain)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.tbSweepLength)).BeginInit();
+ this.grbFrq.SuspendLayout();
+ this.grpFrqEnd.SuspendLayout();
+ this.groupBox1.SuspendLayout();
+ this.grpSweepLength.SuspendLayout();
+ this.grpPhaseReverse.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // btnStart
+ //
+ this.btnStart.Location = new System.Drawing.Point(15, 17);
+ this.btnStart.Name = "btnStart";
+ this.btnStart.Size = new System.Drawing.Size(65, 27);
+ this.btnStart.TabIndex = 0;
+ this.btnStart.Text = "Start";
+ this.btnStart.UseVisualStyleBackColor = true;
+ this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
+ //
+ // btnStop
+ //
+ this.btnStop.Location = new System.Drawing.Point(86, 17);
+ this.btnStop.Name = "btnStop";
+ this.btnStop.Size = new System.Drawing.Size(65, 27);
+ this.btnStop.TabIndex = 0;
+ this.btnStop.Text = "Stop";
+ this.btnStop.UseVisualStyleBackColor = true;
+ this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
+ //
+ // cmbType
+ //
+ this.cmbType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cmbType.FormattingEnabled = true;
+ this.cmbType.Location = new System.Drawing.Point(330, 24);
+ this.cmbType.Name = "cmbType";
+ this.cmbType.Size = new System.Drawing.Size(191, 21);
+ this.cmbType.TabIndex = 1;
+ this.cmbType.SelectedIndexChanged += new System.EventHandler(this.cmbType_SelectedIndexChanged);
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(277, 24);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(31, 13);
+ this.label1.TabIndex = 2;
+ this.label1.Text = "Type";
+ //
+ // tbFrq
+ //
+ this.tbFrq.Location = new System.Drawing.Point(6, 70);
+ this.tbFrq.Margin = new System.Windows.Forms.Padding(0);
+ this.tbFrq.Maximum = 272;
+ this.tbFrq.Name = "tbFrq";
+ this.tbFrq.Size = new System.Drawing.Size(272, 45);
+ this.tbFrq.TabIndex = 3;
+ this.tbFrq.TickStyle = System.Windows.Forms.TickStyle.None;
+ this.tbFrq.Scroll += new System.EventHandler(this.tbFrq_Scroll);
+ //
+ // lblFrq
+ //
+ this.lblFrq.AutoSize = true;
+ this.lblFrq.Location = new System.Drawing.Point(292, 72);
+ this.lblFrq.Name = "lblFrq";
+ this.lblFrq.Size = new System.Drawing.Size(37, 13);
+ this.lblFrq.TabIndex = 4;
+ this.lblFrq.Text = "00000";
+ //
+ // lblFrqTitle
+ //
+ this.lblFrqTitle.AutoSize = true;
+ this.lblFrqTitle.Location = new System.Drawing.Point(10, 54);
+ this.lblFrqTitle.Name = "lblFrqTitle";
+ this.lblFrqTitle.Size = new System.Drawing.Size(57, 13);
+ this.lblFrqTitle.TabIndex = 2;
+ this.lblFrqTitle.Text = "Frequency";
+ //
+ // lblFrqEndTitle
+ //
+ this.lblFrqEndTitle.AutoSize = true;
+ this.lblFrqEndTitle.Location = new System.Drawing.Point(6, 48);
+ this.lblFrqEndTitle.Name = "lblFrqEndTitle";
+ this.lblFrqEndTitle.Size = new System.Drawing.Size(57, 13);
+ this.lblFrqEndTitle.TabIndex = 2;
+ this.lblFrqEndTitle.Text = "Frequency";
+ //
+ // tbFrqEnd
+ //
+ this.tbFrqEnd.Location = new System.Drawing.Point(2, 64);
+ this.tbFrqEnd.Maximum = 1000;
+ this.tbFrqEnd.Name = "tbFrqEnd";
+ this.tbFrqEnd.Size = new System.Drawing.Size(272, 45);
+ this.tbFrqEnd.TabIndex = 3;
+ this.tbFrqEnd.TickStyle = System.Windows.Forms.TickStyle.None;
+ this.tbFrqEnd.Scroll += new System.EventHandler(this.tbFrqEnd_Scroll);
+ //
+ // lblFrqEnd
+ //
+ this.lblFrqEnd.AutoSize = true;
+ this.lblFrqEnd.Location = new System.Drawing.Point(288, 66);
+ this.lblFrqEnd.Name = "lblFrqEnd";
+ this.lblFrqEnd.Size = new System.Drawing.Size(37, 13);
+ this.lblFrqEnd.TabIndex = 4;
+ this.lblFrqEnd.Text = "00000";
+ //
+ // lblFrqEndUnit
+ //
+ this.lblFrqEndUnit.AutoSize = true;
+ this.lblFrqEndUnit.Location = new System.Drawing.Point(331, 66);
+ this.lblFrqEndUnit.Name = "lblFrqEndUnit";
+ this.lblFrqEndUnit.Size = new System.Drawing.Size(20, 13);
+ this.lblFrqEndUnit.TabIndex = 4;
+ this.lblFrqEndUnit.Text = "Hz";
+ //
+ // tbGain
+ //
+ this.tbGain.Location = new System.Drawing.Point(2, 40);
+ this.tbGain.Maximum = 0;
+ this.tbGain.Minimum = -100;
+ this.tbGain.Name = "tbGain";
+ this.tbGain.Size = new System.Drawing.Size(216, 45);
+ this.tbGain.TabIndex = 3;
+ this.tbGain.TickStyle = System.Windows.Forms.TickStyle.None;
+ this.tbGain.Value = -50;
+ this.tbGain.Scroll += new System.EventHandler(this.tbGain_Scroll);
+ //
+ // lblGain
+ //
+ this.lblGain.AutoSize = true;
+ this.lblGain.Location = new System.Drawing.Point(224, 40);
+ this.lblGain.Name = "lblGain";
+ this.lblGain.Size = new System.Drawing.Size(37, 13);
+ this.lblGain.TabIndex = 4;
+ this.lblGain.Text = "00000";
+ //
+ // label8
+ //
+ this.label8.AutoSize = true;
+ this.label8.Location = new System.Drawing.Point(267, 40);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(20, 13);
+ this.label8.TabIndex = 4;
+ this.label8.Text = "dB";
+ //
+ // tbSweepLength
+ //
+ this.tbSweepLength.Location = new System.Drawing.Point(6, 19);
+ this.tbSweepLength.Maximum = 30;
+ this.tbSweepLength.Minimum = 1;
+ this.tbSweepLength.Name = "tbSweepLength";
+ this.tbSweepLength.Size = new System.Drawing.Size(212, 45);
+ this.tbSweepLength.TabIndex = 3;
+ this.tbSweepLength.TickStyle = System.Windows.Forms.TickStyle.None;
+ this.tbSweepLength.Value = 10;
+ this.tbSweepLength.Scroll += new System.EventHandler(this.tbSweepLength_Scroll);
+ //
+ // lblSweepLength
+ //
+ this.lblSweepLength.AutoSize = true;
+ this.lblSweepLength.Location = new System.Drawing.Point(224, 16);
+ this.lblSweepLength.Name = "lblSweepLength";
+ this.lblSweepLength.Size = new System.Drawing.Size(37, 13);
+ this.lblSweepLength.TabIndex = 4;
+ this.lblSweepLength.Text = "00000";
+ //
+ // lblSweepLengthUnit
+ //
+ this.lblSweepLengthUnit.AutoSize = true;
+ this.lblSweepLengthUnit.Location = new System.Drawing.Point(267, 16);
+ this.lblSweepLengthUnit.Name = "lblSweepLengthUnit";
+ this.lblSweepLengthUnit.Size = new System.Drawing.Size(12, 13);
+ this.lblSweepLengthUnit.TabIndex = 4;
+ this.lblSweepLengthUnit.Text = "s";
+ //
+ // chkReverseLeft
+ //
+ this.chkReverseLeft.AutoSize = true;
+ this.chkReverseLeft.Location = new System.Drawing.Point(108, 19);
+ this.chkReverseLeft.Name = "chkReverseLeft";
+ this.chkReverseLeft.Size = new System.Drawing.Size(44, 17);
+ this.chkReverseLeft.TabIndex = 5;
+ this.chkReverseLeft.Text = "Left";
+ this.chkReverseLeft.UseVisualStyleBackColor = true;
+ this.chkReverseLeft.CheckedChanged += new System.EventHandler(this.chkReverseLeft_CheckedChanged);
+ //
+ // chkReverseRight
+ //
+ this.chkReverseRight.AutoSize = true;
+ this.chkReverseRight.Location = new System.Drawing.Point(167, 19);
+ this.chkReverseRight.Name = "chkReverseRight";
+ this.chkReverseRight.Size = new System.Drawing.Size(51, 17);
+ this.chkReverseRight.TabIndex = 5;
+ this.chkReverseRight.Text = "Right";
+ this.chkReverseRight.UseVisualStyleBackColor = true;
+ this.chkReverseRight.CheckedChanged += new System.EventHandler(this.chkReverseRight_CheckedChanged);
+ //
+ // cmbPrecisionFrq
+ //
+ this.cmbPrecisionFrq.FormattingEnabled = true;
+ this.cmbPrecisionFrq.Items.AddRange(new object[] {
+ "Octave",
+ "1/2 Octave",
+ "1/3 Octave",
+ "1/6 Octave",
+ "1/12 Octave",
+ "1/24 Octave",
+ "1/48 Octave"});
+ this.cmbPrecisionFrq.Location = new System.Drawing.Point(159, 51);
+ this.cmbPrecisionFrq.Name = "cmbPrecisionFrq";
+ this.cmbPrecisionFrq.Size = new System.Drawing.Size(108, 21);
+ this.cmbPrecisionFrq.TabIndex = 1;
+ this.cmbPrecisionFrq.SelectedIndexChanged += new System.EventHandler(this.cmbPrecisionFrq_SelectedIndexChanged);
+ //
+ // lblFrqPrecision
+ //
+ this.lblFrqPrecision.AutoSize = true;
+ this.lblFrqPrecision.Location = new System.Drawing.Point(96, 54);
+ this.lblFrqPrecision.Name = "lblFrqPrecision";
+ this.lblFrqPrecision.Size = new System.Drawing.Size(50, 13);
+ this.lblFrqPrecision.TabIndex = 2;
+ this.lblFrqPrecision.Text = "Precision";
+ //
+ // cmbPrecisionFrqEnd
+ //
+ this.cmbPrecisionFrqEnd.FormattingEnabled = true;
+ this.cmbPrecisionFrqEnd.Items.AddRange(new object[] {
+ "Octave",
+ "1/2 Octave",
+ "1/3 Octave",
+ "1/6 Octave",
+ "1/12 Octave",
+ "1/24 Octave",
+ "1/48 Octave"});
+ this.cmbPrecisionFrqEnd.Location = new System.Drawing.Point(155, 45);
+ this.cmbPrecisionFrqEnd.Name = "cmbPrecisionFrqEnd";
+ this.cmbPrecisionFrqEnd.Size = new System.Drawing.Size(108, 21);
+ this.cmbPrecisionFrqEnd.TabIndex = 1;
+ this.cmbPrecisionFrqEnd.SelectedIndexChanged += new System.EventHandler(this.cmbPrecisionFrqEnd_SelectedIndexChanged);
+ //
+ // lblFrqEndPrecision
+ //
+ this.lblFrqEndPrecision.AutoSize = true;
+ this.lblFrqEndPrecision.Location = new System.Drawing.Point(91, 48);
+ this.lblFrqEndPrecision.Name = "lblFrqEndPrecision";
+ this.lblFrqEndPrecision.Size = new System.Drawing.Size(50, 13);
+ this.lblFrqEndPrecision.TabIndex = 2;
+ this.lblFrqEndPrecision.Text = "Precision";
+ //
+ // lblFrqUnit
+ //
+ this.lblFrqUnit.AutoSize = true;
+ this.lblFrqUnit.Location = new System.Drawing.Point(335, 72);
+ this.lblFrqUnit.Name = "lblFrqUnit";
+ this.lblFrqUnit.Size = new System.Drawing.Size(20, 13);
+ this.lblFrqUnit.TabIndex = 4;
+ this.lblFrqUnit.Text = "Hz";
+ //
+ // cmbFrq
+ //
+ this.cmbFrq.FormattingEnabled = true;
+ this.cmbFrq.Items.AddRange(new object[] {
+ "Variable",
+ "16",
+ "20",
+ "25",
+ "31.5",
+ "40",
+ "50",
+ "63",
+ "80",
+ "100",
+ "125",
+ "160",
+ "200",
+ "250",
+ "315",
+ "400",
+ "440",
+ "500",
+ "630",
+ "800",
+ "1000",
+ "1250",
+ "1600",
+ "2000",
+ "2500",
+ "3150",
+ "4000",
+ "5000",
+ "6300",
+ "8000",
+ "9600",
+ "10000",
+ "11000",
+ "16000",
+ "20000"});
+ this.cmbFrq.Location = new System.Drawing.Point(77, 22);
+ this.cmbFrq.Name = "cmbFrq";
+ this.cmbFrq.Size = new System.Drawing.Size(108, 21);
+ this.cmbFrq.TabIndex = 1;
+ this.cmbFrq.SelectedIndexChanged += new System.EventHandler(this.cmbFrq_SelectedIndexChanged);
+ //
+ // grbFrq
+ //
+ this.grbFrq.Controls.Add(this.cmbFrq);
+ this.grbFrq.Controls.Add(this.tbFrq);
+ this.grbFrq.Controls.Add(this.cmbPrecisionFrq);
+ this.grbFrq.Controls.Add(this.lblFrqUnit);
+ this.grbFrq.Controls.Add(this.lblFrqTitle);
+ this.grbFrq.Controls.Add(this.lblPreset);
+ this.grbFrq.Controls.Add(this.lblFrqPrecision);
+ this.grbFrq.Controls.Add(this.lblFrq);
+ this.grbFrq.Location = new System.Drawing.Point(5, 51);
+ this.grbFrq.Name = "grbFrq";
+ this.grbFrq.Size = new System.Drawing.Size(355, 118);
+ this.grbFrq.TabIndex = 6;
+ this.grbFrq.TabStop = false;
+ this.grbFrq.Text = "Start Frequency";
+ //
+ // lblPreset
+ //
+ this.lblPreset.AutoSize = true;
+ this.lblPreset.Location = new System.Drawing.Point(14, 25);
+ this.lblPreset.Name = "lblPreset";
+ this.lblPreset.Size = new System.Drawing.Size(37, 13);
+ this.lblPreset.TabIndex = 2;
+ this.lblPreset.Text = "Preset";
+ //
+ // grpFrqEnd
+ //
+ this.grpFrqEnd.Controls.Add(this.cmbFrqEnd);
+ this.grpFrqEnd.Controls.Add(this.lblEndPreset);
+ this.grpFrqEnd.Controls.Add(this.lblFrqEndTitle);
+ this.grpFrqEnd.Controls.Add(this.cmbPrecisionFrqEnd);
+ this.grpFrqEnd.Controls.Add(this.lblFrqEndPrecision);
+ this.grpFrqEnd.Controls.Add(this.tbFrqEnd);
+ this.grpFrqEnd.Controls.Add(this.lblFrqEndUnit);
+ this.grpFrqEnd.Controls.Add(this.lblFrqEnd);
+ this.grpFrqEnd.Location = new System.Drawing.Point(5, 175);
+ this.grpFrqEnd.Name = "grpFrqEnd";
+ this.grpFrqEnd.Size = new System.Drawing.Size(355, 111);
+ this.grpFrqEnd.TabIndex = 7;
+ this.grpFrqEnd.TabStop = false;
+ this.grpFrqEnd.Text = "End Frequency";
+ //
+ // cmbFrqEnd
+ //
+ this.cmbFrqEnd.FormattingEnabled = true;
+ this.cmbFrqEnd.Items.AddRange(new object[] {
+ "Variable",
+ "16",
+ "20",
+ "25",
+ "31.5",
+ "40",
+ "50",
+ "63",
+ "80",
+ "100",
+ "125",
+ "160",
+ "200",
+ "250",
+ "315",
+ "400",
+ "440",
+ "500",
+ "630",
+ "800",
+ "1000",
+ "1250",
+ "1600",
+ "2000",
+ "2500",
+ "3150",
+ "4000",
+ "5000",
+ "6300",
+ "8000",
+ "9600",
+ "10000",
+ "11000",
+ "16000",
+ "20000"});
+ this.cmbFrqEnd.Location = new System.Drawing.Point(74, 19);
+ this.cmbFrqEnd.Name = "cmbFrqEnd";
+ this.cmbFrqEnd.Size = new System.Drawing.Size(108, 21);
+ this.cmbFrqEnd.TabIndex = 5;
+ this.cmbFrqEnd.SelectedIndexChanged += new System.EventHandler(this.cmbFrqEnd_SelectedIndexChanged);
+ //
+ // lblEndPreset
+ //
+ this.lblEndPreset.AutoSize = true;
+ this.lblEndPreset.Location = new System.Drawing.Point(11, 22);
+ this.lblEndPreset.Name = "lblEndPreset";
+ this.lblEndPreset.Size = new System.Drawing.Size(37, 13);
+ this.lblEndPreset.TabIndex = 6;
+ this.lblEndPreset.Text = "Preset";
+ //
+ // groupBox1
+ //
+ this.groupBox1.Controls.Add(this.tbGain);
+ this.groupBox1.Controls.Add(this.lblGain);
+ this.groupBox1.Controls.Add(this.label8);
+ this.groupBox1.Location = new System.Drawing.Point(366, 51);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new System.Drawing.Size(287, 90);
+ this.groupBox1.TabIndex = 8;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "Gain";
+ //
+ // grpSweepLength
+ //
+ this.grpSweepLength.Controls.Add(this.tbSweepLength);
+ this.grpSweepLength.Controls.Add(this.lblSweepLength);
+ this.grpSweepLength.Controls.Add(this.lblSweepLengthUnit);
+ this.grpSweepLength.Location = new System.Drawing.Point(366, 147);
+ this.grpSweepLength.Name = "grpSweepLength";
+ this.grpSweepLength.Size = new System.Drawing.Size(287, 80);
+ this.grpSweepLength.TabIndex = 9;
+ this.grpSweepLength.TabStop = false;
+ this.grpSweepLength.Text = "Sweep Length (seconds)";
+ //
+ // grpPhaseReverse
+ //
+ this.grpPhaseReverse.Controls.Add(this.chkReverseLeft);
+ this.grpPhaseReverse.Controls.Add(this.chkReverseRight);
+ this.grpPhaseReverse.Location = new System.Drawing.Point(366, 233);
+ this.grpPhaseReverse.Name = "grpPhaseReverse";
+ this.grpPhaseReverse.Size = new System.Drawing.Size(287, 51);
+ this.grpPhaseReverse.TabIndex = 10;
+ this.grpPhaseReverse.TabStop = false;
+ this.grpPhaseReverse.Text = "PhaseReverse";
+ //
+ // buttonSave
+ //
+ this.buttonSave.Location = new System.Drawing.Point(157, 17);
+ this.buttonSave.Name = "buttonSave";
+ this.buttonSave.Size = new System.Drawing.Size(65, 27);
+ this.buttonSave.TabIndex = 0;
+ this.buttonSave.Text = "Save";
+ this.buttonSave.UseVisualStyleBackColor = true;
+ this.buttonSave.Click += new System.EventHandler(this.buttonSave_Click);
+ //
+ // GeneratorPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.AutoScroll = true;
+ this.AutoScrollMinSize = new System.Drawing.Size(674, 296);
+ this.Controls.Add(this.grpPhaseReverse);
+ this.Controls.Add(this.grpSweepLength);
+ this.Controls.Add(this.groupBox1);
+ this.Controls.Add(this.grpFrqEnd);
+ this.Controls.Add(this.grbFrq);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.cmbType);
+ this.Controls.Add(this.buttonSave);
+ this.Controls.Add(this.btnStop);
+ this.Controls.Add(this.btnStart);
+ this.Name = "GeneratorPanel";
+ this.Size = new System.Drawing.Size(675, 298);
+ ((System.ComponentModel.ISupportInitialize)(this.tbFrq)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.tbFrqEnd)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.tbGain)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.tbSweepLength)).EndInit();
+ this.grbFrq.ResumeLayout(false);
+ this.grbFrq.PerformLayout();
+ this.grpFrqEnd.ResumeLayout(false);
+ this.grpFrqEnd.PerformLayout();
+ this.groupBox1.ResumeLayout(false);
+ this.groupBox1.PerformLayout();
+ this.grpSweepLength.ResumeLayout(false);
+ this.grpSweepLength.PerformLayout();
+ this.grpPhaseReverse.ResumeLayout(false);
+ this.grpPhaseReverse.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button btnStart;
+ private System.Windows.Forms.Button btnStop;
+ private System.Windows.Forms.ComboBox cmbType;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.TrackBar tbFrq;
+ private System.Windows.Forms.Label lblFrq;
+ private System.Windows.Forms.Label lblFrqTitle;
+ private System.Windows.Forms.Label lblFrqEndTitle;
+ private System.Windows.Forms.TrackBar tbFrqEnd;
+ private System.Windows.Forms.Label lblFrqEnd;
+ private System.Windows.Forms.Label lblFrqEndUnit;
+ private System.Windows.Forms.TrackBar tbGain;
+ private System.Windows.Forms.Label lblGain;
+ private System.Windows.Forms.Label label8;
+ private System.Windows.Forms.TrackBar tbSweepLength;
+ private System.Windows.Forms.Label lblSweepLength;
+ private System.Windows.Forms.Label lblSweepLengthUnit;
+ private System.Windows.Forms.CheckBox chkReverseLeft;
+ private System.Windows.Forms.CheckBox chkReverseRight;
+ private System.Windows.Forms.ComboBox cmbPrecisionFrq;
+ private System.Windows.Forms.Label lblFrqPrecision;
+ private System.Windows.Forms.ComboBox cmbPrecisionFrqEnd;
+ private System.Windows.Forms.Label lblFrqEndPrecision;
+ private System.Windows.Forms.Label lblFrqUnit;
+ private System.Windows.Forms.ComboBox cmbFrq;
+ private System.Windows.Forms.GroupBox grbFrq;
+ private System.Windows.Forms.Label lblPreset;
+ private System.Windows.Forms.GroupBox grpFrqEnd;
+ private System.Windows.Forms.ComboBox cmbFrqEnd;
+ private System.Windows.Forms.Label lblEndPreset;
+ private System.Windows.Forms.GroupBox groupBox1;
+ private System.Windows.Forms.GroupBox grpSweepLength;
+ private System.Windows.Forms.GroupBox grpPhaseReverse;
+ private System.Windows.Forms.Button buttonSave;
+ }
+}
diff --git a/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.cs b/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.cs
new file mode 100644
index 00000000..76c77876
--- /dev/null
+++ b/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.cs
@@ -0,0 +1,371 @@
+using NAudio.Utils;
+using NAudio.Wave;
+using System;
+using System.Windows.Forms;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudioDemo.Generator
+{
+ public partial class GeneratorPanel : UserControl
+ {
+
+ private WaveOut driverOut;
+
+ private SignalGenerator wg;
+
+ // Frequency Max
+ private const double FMax = 20000;
+
+ // Frequency Min
+ private const double FMin = 20;
+
+ // constante Math.Log10(FMax / FMin)
+ private double Log10FmaxFMin;
+
+
+ public GeneratorPanel()
+ {
+ // Const
+ Log10FmaxFMin = Math.Log10(FMax/FMin);
+
+ // Panel Init
+ InitializeComponent();
+ this.Disposed += new EventHandler(GeneratorPanel_Disposed);
+
+ // Init Audio
+ driverOut = new WaveOut();
+ driverOut.DesiredLatency = 100;
+ //driverOut = new AsioOut(0);
+ wg = new SignalGenerator();
+
+ // Par Default Frq 1200Hz
+ cmbFrq.SelectedIndex = 0;
+ cmbPrecisionFrq.SelectedIndex = 2;
+ tbFrq.Value = 12; // 1200Hz
+ tbToFrq();
+
+ // Par Default Frq End 2000Hz
+ cmbFrqEnd.SelectedIndex = 0;
+ cmbPrecisionFrqEnd.SelectedIndex = 2;
+ tbFrqEnd.Value = tbFrqEnd.Maximum;
+ tbToFrqEnd();
+
+ // comboBox Type
+ cmbType.DataSource = Enum.GetValues(typeof (SignalGeneratorType));
+ cmbType.SelectedIndex = 0;
+
+ // Par Default Gain -20dB
+ tbGain.Value = -20;
+ tbToGain();
+
+ // Par Default SweepSeconds
+ tbSweepLength.Value = 10;
+ tbToSweepLength();
+
+ // Init Driver Audio
+ driverOut.Init(wg);
+ StartStopEnabled();
+
+ }
+
+ private void GeneratorPanel_Disposed(object sender, EventArgs e)
+ {
+ Cleanup();
+ }
+
+ // --------------
+ // Start, Stop
+ // --------------
+
+ // btn Start
+ private void btnStart_Click(object sender, EventArgs e)
+ {
+ if (driverOut != null)
+ {
+ driverOut.Play();
+ StartStopEnabled();
+ }
+ }
+
+ // btn Stop
+ private void btnStop_Click(object sender, EventArgs e)
+ {
+ if (driverOut != null)
+ {
+ driverOut.Stop();
+ StartStopEnabled();
+ }
+
+
+ }
+
+ // Bouton Enabled
+ private void StartStopEnabled()
+ {
+ bool bDriverReady = (driverOut != null);
+
+ btnStart.Enabled = bDriverReady & (driverOut.PlaybackState == PlaybackState.Stopped);
+ btnStop.Enabled = bDriverReady & (driverOut.PlaybackState == PlaybackState.Playing);
+ buttonSave.Enabled = btnStart.Enabled;
+
+ }
+
+ // --------------
+ // Type Generator
+ // --------------
+
+ // cmb Type
+ private void cmbType_SelectedIndexChanged(object sender, EventArgs e)
+ {
+
+ FrqEnabled(false);
+ FrqEndEnabled(false);
+ SweepLengthEnabled(false);
+
+ switch ((SignalGeneratorType) cmbType.SelectedItem)
+ {
+ case SignalGeneratorType.Sin:
+ case SignalGeneratorType.Square:
+ case SignalGeneratorType.Triangle:
+ case SignalGeneratorType.SawTooth:
+ FrqEnabled(true);
+ break;
+
+ case SignalGeneratorType.Sweep:
+ FrqEnabled(true);
+ FrqEndEnabled(true);
+ SweepLengthEnabled(true);
+ break;
+
+ default:
+ break;
+ }
+
+ wg.Type = (SignalGeneratorType) cmbType.SelectedItem;
+ }
+
+ // --------------
+ // Frequency
+ // --------------
+
+ // cmbFrq
+ private void cmbFrq_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ tbToFrq();
+ FrqEnabled(true);
+ }
+
+ // trackbar Frq
+ private void tbFrq_Scroll(object sender, EventArgs e)
+ {
+ tbToFrq();
+ }
+
+ // comboBox Precision Frq
+ private void cmbPrecisionFrq_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ // change Type
+ int octave = cmbPrecisionToOctave(cmbPrecisionFrq.SelectedIndex);
+
+ // change tbFrq
+ tbFrq.Maximum = octave;
+
+ //
+ tbToFrq();
+ }
+
+
+ // Calcul TaskBar to Frq
+ private void tbToFrq()
+ {
+ double x = Math.Pow(10, (tbFrq.Value/(tbFrq.Maximum/Log10FmaxFMin)))*FMin;
+ x = Math.Round(x, 1);
+
+
+ // Change Frequency in Generator
+ if (cmbFrq.SelectedIndex <= 0)
+ wg.Frequency = x;
+ else
+ wg.Frequency = Convert.ToDouble(cmbFrq.SelectedItem);
+
+ // View Frq
+ lblFrq.Text = x.ToString();
+ }
+
+ // Frq Enabled
+ private void FrqEnabled(bool state)
+ {
+ grbFrq.Enabled = state;
+ bool bFrqVariable = (cmbFrq.SelectedIndex <= 0);
+ tbFrq.Enabled = bFrqVariable;
+ cmbPrecisionFrq.Enabled = bFrqVariable;
+ lblFrqPrecision.Enabled = bFrqVariable;
+ lblFrq.Enabled = bFrqVariable;
+ lblFrqUnit.Enabled = bFrqVariable;
+ lblFrqTitle.Enabled = bFrqVariable;
+ }
+
+ // --------------
+ // Frequency End
+ // --------------
+
+ // cmb Frq End
+ private void cmbFrqEnd_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ tbToFrqEnd();
+ FrqEndEnabled(true);
+ }
+
+ // trackbar FrqEnd
+ private void tbFrqEnd_Scroll(object sender, EventArgs e)
+ {
+ tbToFrqEnd();
+ }
+
+ // combobox FrqEnd Precision
+ private void cmbPrecisionFrqEnd_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ // change Type
+ int octave = cmbPrecisionToOctave(cmbPrecisionFrqEnd.SelectedIndex);
+
+ // change tbFrq
+ tbFrqEnd.Maximum = octave;
+
+ //
+ tbToFrqEnd();
+ }
+
+ // Calcul TaskBar to Frq
+ private void tbToFrqEnd()
+ {
+ double x = Math.Pow(10, (tbFrqEnd.Value/(tbFrqEnd.Maximum/Log10FmaxFMin)))*FMin;
+ x = Math.Round(x, 1);
+
+ // Change Frequency in Generator
+ if (cmbFrqEnd.SelectedIndex <= 0)
+ wg.FrequencyEnd = x;
+ else
+ wg.FrequencyEnd = Convert.ToDouble(cmbFrqEnd.SelectedItem);
+
+
+ // View Frq
+ lblFrqEnd.Text = x.ToString();
+ }
+
+ // FrqEnd Enabled
+ private void FrqEndEnabled(bool state)
+ {
+ grpFrqEnd.Enabled = state;
+ bool bFrqEndVariable = (cmbFrqEnd.SelectedIndex <= 0);
+ tbFrqEnd.Enabled = bFrqEndVariable;
+ cmbPrecisionFrqEnd.Enabled = bFrqEndVariable;
+ lblFrqEndPrecision.Enabled = bFrqEndVariable;
+ lblFrqEnd.Enabled = bFrqEndVariable;
+ lblFrqEndUnit.Enabled = bFrqEndVariable;
+ lblFrqEndTitle.Enabled = bFrqEndVariable;
+ }
+
+ // --------------
+ // Gain
+ // --------------
+
+ // trackbar Gain
+ private void tbGain_Scroll(object sender, EventArgs e)
+ {
+ tbToGain();
+ }
+
+ // Calcul TaskBar to Gain
+ private void tbToGain()
+ {
+ lblGain.Text = tbGain.Value.ToString();
+ wg.Gain = Decibels.DecibelsToLinear(tbGain.Value);
+ }
+
+ // --------------
+ // Sweep Length
+ // --------------
+
+ // trackbar Sweep Length
+ private void tbSweepLength_Scroll(object sender, EventArgs e)
+ {
+ tbToSweepLength();
+ }
+
+ // Calcul TaskBar to Length
+ private void tbToSweepLength()
+ {
+ lblSweepLength.Text = tbSweepLength.Value.ToString();
+ wg.SweepLengthSecs = tbSweepLength.Value;
+
+ }
+
+ // Sweep Length Enabled
+ private void SweepLengthEnabled(bool state)
+ {
+ grpSweepLength.Enabled = state;
+ }
+
+ // --------------
+ // Phase Reverse
+ // --------------
+
+ // Reverse Left
+ private void chkReverseLeft_CheckedChanged(object sender, EventArgs e)
+ {
+ PhaseReverse();
+ }
+
+ // Reverse Right
+ private void chkReverseRight_CheckedChanged(object sender, EventArgs e)
+ {
+ PhaseReverse();
+ }
+
+ // Apply PhaseReverse
+ private void PhaseReverse()
+ {
+ wg.PhaseReverse[0] = chkReverseLeft.Checked;
+ wg.PhaseReverse[1] = chkReverseRight.Checked;
+ }
+
+ // --------------
+ // Other
+ // --------------
+
+ // Nb of Frequency
+ private int cmbPrecisionToOctave(int idx)
+ {
+ return (int) (10.35f*idx);
+ }
+
+ // Clean DriverOut
+ private void Cleanup()
+ {
+ if (driverOut != null)
+ driverOut.Stop();
+
+ if (wg != null)
+ wg = null;
+
+ if (driverOut != null)
+ {
+ driverOut.Dispose();
+ }
+ }
+
+ private void buttonSave_Click(object sender, EventArgs e)
+ {
+ btnStop_Click(this,e);
+ var sfd = new SaveFileDialog();
+ sfd.Filter = "WAV File|*.wav";
+ if (sfd.ShowDialog() == DialogResult.OK)
+ {
+ var osp = new OffsetSampleProvider(wg);
+ osp.TakeSamples = wg.WaveFormat.SampleRate*20*wg.WaveFormat.Channels;
+ WaveFileWriter.CreateWaveFile16(sfd.FileName, osp);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.resx b/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.resx
new file mode 100644
index 00000000..5ea0895e
--- /dev/null
+++ b/NAudioDemo/SignalGeneratorDemo/GeneratorPanel.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/NAudioDemo/SignalGeneratorDemo/GeneratorPlugIn.cs b/NAudioDemo/SignalGeneratorDemo/GeneratorPlugIn.cs
new file mode 100644
index 00000000..25bc3155
--- /dev/null
+++ b/NAudioDemo/SignalGeneratorDemo/GeneratorPlugIn.cs
@@ -0,0 +1,19 @@
+using System.ComponentModel.Composition;
+using System.Windows.Forms;
+
+namespace NAudioDemo.Generator
+{
+ [Export(typeof (INAudioDemoPlugin))]
+ internal class GeneratorPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "Signal Generator"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new GeneratorPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.Designer.cs b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.Designer.cs
new file mode 100644
index 00000000..1e42f542
--- /dev/null
+++ b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.Designer.cs
@@ -0,0 +1,174 @@
+namespace NAudioDemo.SimplePlaybackDemo
+{
+ partial class SimplePlaybackPanel
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.label1 = new System.Windows.Forms.Label();
+ this.buttonPlay = new System.Windows.Forms.Button();
+ this.buttonStop = new System.Windows.Forms.Button();
+ this.comboBoxOutputDriver = new System.Windows.Forms.ComboBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.buttonOpen = new System.Windows.Forms.Button();
+ this.labelNowTime = new System.Windows.Forms.Label();
+ this.labelTotalTime = new System.Windows.Forms.Label();
+ this.timer1 = new System.Windows.Forms.Timer(this.components);
+ this.button1 = new System.Windows.Forms.Button();
+ this.volumeSlider1 = new NAudio.Gui.VolumeSlider();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(4, 4);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(652, 13);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "This is an experimental WORK IN PROGRESS. Will demo use of new AudioFileReader cl" +
+ "ass and used to debug PlaybackStopped event";
+ //
+ // buttonPlay
+ //
+ this.buttonPlay.Location = new System.Drawing.Point(91, 31);
+ this.buttonPlay.Name = "buttonPlay";
+ this.buttonPlay.Size = new System.Drawing.Size(75, 23);
+ this.buttonPlay.TabIndex = 1;
+ this.buttonPlay.Text = "Play";
+ this.buttonPlay.UseVisualStyleBackColor = true;
+ this.buttonPlay.Click += new System.EventHandler(this.buttonPlay_Click);
+ //
+ // buttonStop
+ //
+ this.buttonStop.Location = new System.Drawing.Point(173, 30);
+ this.buttonStop.Name = "buttonStop";
+ this.buttonStop.Size = new System.Drawing.Size(75, 23);
+ this.buttonStop.TabIndex = 2;
+ this.buttonStop.Text = "Stop";
+ this.buttonStop.UseVisualStyleBackColor = true;
+ this.buttonStop.Click += new System.EventHandler(this.buttonStop_Click);
+ //
+ // comboBoxOutputDriver
+ //
+ this.comboBoxOutputDriver.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.comboBoxOutputDriver.FormattingEnabled = true;
+ this.comboBoxOutputDriver.Location = new System.Drawing.Point(128, 80);
+ this.comboBoxOutputDriver.Name = "comboBoxOutputDriver";
+ this.comboBoxOutputDriver.Size = new System.Drawing.Size(227, 21);
+ this.comboBoxOutputDriver.TabIndex = 3;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(7, 83);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(104, 13);
+ this.label2.TabIndex = 4;
+ this.label2.Text = "For Debug Purposes";
+ //
+ // buttonOpen
+ //
+ this.buttonOpen.Location = new System.Drawing.Point(10, 31);
+ this.buttonOpen.Name = "buttonOpen";
+ this.buttonOpen.Size = new System.Drawing.Size(75, 23);
+ this.buttonOpen.TabIndex = 1;
+ this.buttonOpen.Text = "Open";
+ this.buttonOpen.UseVisualStyleBackColor = true;
+ this.buttonOpen.Click += new System.EventHandler(this.buttonOpen_Click);
+ //
+ // labelNowTime
+ //
+ this.labelNowTime.AutoSize = true;
+ this.labelNowTime.Location = new System.Drawing.Point(436, 40);
+ this.labelNowTime.Name = "labelNowTime";
+ this.labelNowTime.Size = new System.Drawing.Size(34, 13);
+ this.labelNowTime.TabIndex = 6;
+ this.labelNowTime.Text = "00:00";
+ //
+ // labelTotalTime
+ //
+ this.labelTotalTime.AutoSize = true;
+ this.labelTotalTime.Location = new System.Drawing.Point(476, 40);
+ this.labelTotalTime.Name = "labelTotalTime";
+ this.labelTotalTime.Size = new System.Drawing.Size(34, 13);
+ this.labelTotalTime.TabIndex = 6;
+ this.labelTotalTime.Text = "00:00";
+ //
+ // button1
+ //
+ this.button1.Location = new System.Drawing.Point(10, 123);
+ this.button1.Name = "button1";
+ this.button1.Size = new System.Drawing.Size(101, 23);
+ this.button1.TabIndex = 7;
+ this.button1.Text = "MP3 Reposition";
+ this.button1.UseVisualStyleBackColor = true;
+ this.button1.Click += new System.EventHandler(this.OnMp3RepositionTestClick);
+ //
+ // volumeSlider1
+ //
+ this.volumeSlider1.Location = new System.Drawing.Point(307, 37);
+ this.volumeSlider1.Name = "volumeSlider1";
+ this.volumeSlider1.Size = new System.Drawing.Size(96, 16);
+ this.volumeSlider1.TabIndex = 5;
+ this.volumeSlider1.VolumeChanged += new System.EventHandler(this.volumeSlider1_VolumeChanged);
+ //
+ // SimplePlaybackPanel
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.button1);
+ this.Controls.Add(this.labelTotalTime);
+ this.Controls.Add(this.labelNowTime);
+ this.Controls.Add(this.volumeSlider1);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.comboBoxOutputDriver);
+ this.Controls.Add(this.buttonStop);
+ this.Controls.Add(this.buttonOpen);
+ this.Controls.Add(this.buttonPlay);
+ this.Controls.Add(this.label1);
+ this.Name = "SimplePlaybackPanel";
+ this.Size = new System.Drawing.Size(821, 268);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.Button buttonPlay;
+ private System.Windows.Forms.Button buttonStop;
+ private System.Windows.Forms.ComboBox comboBoxOutputDriver;
+ private System.Windows.Forms.Label label2;
+ private NAudio.Gui.VolumeSlider volumeSlider1;
+ private System.Windows.Forms.Button buttonOpen;
+ private System.Windows.Forms.Label labelNowTime;
+ private System.Windows.Forms.Label labelTotalTime;
+ private System.Windows.Forms.Timer timer1;
+ private System.Windows.Forms.Button button1;
+ }
+}
diff --git a/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.cs b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.cs
new file mode 100644
index 00000000..df9219cd
--- /dev/null
+++ b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using NAudio.FileFormats.Mp3;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudioDemo.SimplePlaybackDemo
+{
+ public partial class SimplePlaybackPanel : UserControl
+ {
+ private IWavePlayer wavePlayer;
+ private AudioFileReader file;
+ private string fileName;
+
+ public SimplePlaybackPanel()
+ {
+ InitializeComponent();
+ EnableButtons(false);
+ PopulateOutputDriverCombo();
+ this.Disposed += new EventHandler(SimplePlaybackPanel_Disposed);
+ this.timer1.Interval = 250;
+ this.timer1.Tick += new EventHandler(timer1_Tick);
+ }
+
+ private static string FormatTimeSpan(TimeSpan ts)
+ {
+ return string.Format("{0:D2}:{1:D2}", (int)ts.TotalMinutes, ts.Seconds);
+ }
+
+ void timer1_Tick(object sender, EventArgs e)
+ {
+ if (file != null)
+ {
+ labelNowTime.Text = FormatTimeSpan(file.CurrentTime);
+ labelTotalTime.Text = FormatTimeSpan(file.TotalTime);
+ }
+ }
+
+ void SimplePlaybackPanel_Disposed(object sender, EventArgs e)
+ {
+ CleanUp();
+ }
+
+ private void PopulateOutputDriverCombo()
+ {
+ comboBoxOutputDriver.Items.Add("WaveOut Window Callbacks");
+ comboBoxOutputDriver.Items.Add("WaveOut Function Callbacks");
+ comboBoxOutputDriver.Items.Add("WaveOut Event Callbacks");
+ comboBoxOutputDriver.SelectedIndex = 0;
+ }
+
+ private void buttonPlay_Click(object sender, EventArgs e)
+ {
+ if (fileName == null) fileName = SelectInputFile();
+ if (fileName != null)
+ {
+ BeginPlayback(fileName);
+ }
+ }
+
+ private static string SelectInputFile()
+ {
+ var ofd = new OpenFileDialog();
+ ofd.Filter = "Audio Files|*.mp3;*.wav;*.aiff;*.wma";
+
+ if (ofd.ShowDialog() == DialogResult.OK)
+ {
+ return ofd.FileName;
+ }
+
+ return null;
+ }
+
+ private void BeginPlayback(string filename)
+ {
+ Debug.Assert(this.wavePlayer == null);
+ this.wavePlayer = CreateWavePlayer();
+ this.file = new AudioFileReader(filename);
+ this.file.Volume = volumeSlider1.Volume;
+ this.wavePlayer.Init(file);
+ this.wavePlayer.PlaybackStopped += wavePlayer_PlaybackStopped;
+ this.wavePlayer.Play();
+ EnableButtons(true);
+ timer1.Enabled = true; // timer for updating current time label
+ }
+
+ private IWavePlayer CreateWavePlayer()
+ {
+ switch (comboBoxOutputDriver.SelectedIndex)
+ {
+ case 2:
+ return new WaveOutEvent();
+ case 1:
+ return new WaveOut(WaveCallbackInfo.FunctionCallback());
+ case 0:
+ default:
+ return new WaveOut();
+ }
+ }
+
+ private void EnableButtons(bool playing)
+ {
+ buttonPlay.Enabled = !playing;
+ buttonStop.Enabled = playing;
+ buttonOpen.Enabled = !playing;
+ }
+
+ void wavePlayer_PlaybackStopped(object sender, StoppedEventArgs e)
+ {
+ // we want to be always on the GUI thread and be able to change GUI components
+ Debug.Assert(!this.InvokeRequired, "PlaybackStopped on wrong thread");
+ // we want it to be safe to clean up input stream and playback device in the handler for PlaybackStopped
+ CleanUp();
+ EnableButtons(false);
+ timer1.Enabled = false;
+ labelNowTime.Text = "00:00";
+ if (e.Exception != null)
+ {
+ MessageBox.Show(String.Format("Playback Stopped due to an error {0}", e.Exception.Message));
+ }
+ }
+
+ private void CleanUp()
+ {
+ if (this.file != null)
+ {
+ this.file.Dispose();
+ this.file = null;
+ }
+ if (this.wavePlayer != null)
+ {
+ this.wavePlayer.Dispose();
+ this.wavePlayer = null;
+ }
+ }
+
+ private void buttonStop_Click(object sender, EventArgs e)
+ {
+ this.wavePlayer.Stop();
+ // don't set button states now, we'll wait for our PlaybackStopped to come
+ }
+
+ private void volumeSlider1_VolumeChanged(object sender, EventArgs e)
+ {
+ if (this.file != null)
+ {
+ this.file.Volume = volumeSlider1.Volume;
+ }
+ }
+
+ private void buttonOpen_Click(object sender, EventArgs e)
+ {
+ fileName = SelectInputFile();
+ }
+
+ private void OnMp3RepositionTestClick(object sender, EventArgs e)
+ {
+ var filename = SelectInputFile();
+ if (filename == null) return;
+ var wo = new WaveOut();
+ var af = new AudioFileReader(filename);
+ wo.Init(af);
+ var f = new Form();
+ var b = new Button() { Text = "Play" };
+ b.Click += (s, a) => wo.Play();
+ var b2 = new Button() { Text = "Stop", Left = b.Right };
+ b2.Click += (s, a) => wo.Stop();
+ var b3 = new Button { Text = "Rewind", Left = b2.Right };
+ b3.Click += (s, a) => af.Position = 0;
+ f.FormClosed += (s, a) => { wo.Dispose(); af.Dispose(); };
+ f.Controls.Add(b);
+ f.Controls.Add(b2);
+ f.Controls.Add(b3);
+ f.ShowDialog(this);
+ }
+ }
+}
diff --git a/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.resx b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.resx
new file mode 100644
index 00000000..6999fbfa
--- /dev/null
+++ b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPanel.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPlugin.cs b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPlugin.cs
new file mode 100644
index 00000000..840b5f2d
--- /dev/null
+++ b/NAudioDemo/SimplePlaybackDemo/SimplePlaybackPlugin.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.ComponentModel.Composition;
+using System.Windows.Forms;
+
+namespace NAudioDemo.SimplePlaybackDemo
+{
+ [Export(typeof(INAudioDemoPlugin))]
+ class SimplePlaybackPlugin : INAudioDemoPlugin
+ {
+ public string Name
+ {
+ get { return "Simple Playback"; }
+ }
+
+ public Control CreatePanel()
+ {
+ return new SimplePlaybackPanel();
+ }
+ }
+}
diff --git a/NAudioDemo/app.config b/NAudioDemo/app.config
new file mode 100644
index 00000000..1aeed1b4
--- /dev/null
+++ b/NAudioDemo/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/NAudioTests/Acm/AcmDriverTests.cs b/NAudioTests/Acm/AcmDriverTests.cs
new file mode 100644
index 00000000..825a9b9e
--- /dev/null
+++ b/NAudioTests/Acm/AcmDriverTests.cs
@@ -0,0 +1,106 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+using NUnit.Framework;
+using System.Diagnostics;
+using NAudio.Wave.Compression;
+
+namespace NAudioTests.Acm
+{
+ [TestFixture]
+ [Category("IntegrationTest")]
+ public class AcmDriverTests
+ {
+ [Test]
+ public void CanEnumerateDrivers()
+ {
+ IEnumerable drivers = AcmDriver.EnumerateAcmDrivers();
+ Assert.IsNotNull(drivers);
+ foreach (AcmDriver driver in drivers)
+ {
+ Assert.GreaterOrEqual((int)driver.DriverId, 0);
+ Assert.IsTrue(!String.IsNullOrEmpty(driver.ShortName));
+ Debug.WriteLine(driver.LongName);
+ }
+ }
+
+ [Test]
+ public void DoesntFindNonexistentCodec()
+ {
+ Assert.IsFalse(AcmDriver.IsCodecInstalled("ASJHASDHJSAK"));
+ }
+
+ [Test]
+ public void FindsStandardCodec()
+ {
+ Assert.IsTrue(AcmDriver.IsCodecInstalled("MS-ADPCM"));
+ }
+
+ [Test]
+ public void HasFindByShortNameMethod()
+ {
+ AcmDriver driver = AcmDriver.FindByShortName("WM-AUDIO");
+ }
+
+ [Test]
+ public void CanOpenAndCloseDriver()
+ {
+ IEnumerable drivers = AcmDriver.EnumerateAcmDrivers();
+ Assert.IsNotNull(drivers);
+ foreach (AcmDriver driver in drivers)
+ {
+ driver.Open();
+ driver.Close();
+ }
+ }
+
+ [Test]
+ public void CanEnumerateFormatTags()
+ {
+ foreach(AcmDriver driver in AcmDriver.EnumerateAcmDrivers())
+ {
+ Debug.WriteLine("Enumerating Format Tags for " + driver.LongName);
+ driver.Open();
+ IEnumerable formatTags = driver.FormatTags;
+ Assert.IsNotNull(formatTags, "FormatTags");
+ foreach(AcmFormatTag formatTag in formatTags)
+ {
+ Debug.WriteLine(String.Format("{0} {1} {2} Standard formats: {3} Support Flags: {4} Format Size: {5}",
+ formatTag.FormatTagIndex,
+ formatTag.FormatTag,
+ formatTag.FormatDescription,
+ formatTag.StandardFormatsCount,
+ formatTag.SupportFlags,
+ formatTag.FormatSize));
+ }
+ driver.Close();
+ }
+ }
+
+ [Test]
+ public void CanEnumerateFormats()
+ {
+ using (AcmDriver driver = AcmDriver.FindByShortName("MS-ADPCM"))
+ {
+ driver.Open();
+ IEnumerable formatTags = driver.FormatTags;
+ Assert.IsNotNull(formatTags, "FormatTags");
+ foreach (AcmFormatTag formatTag in formatTags)
+ {
+ IEnumerable formats = driver.GetFormats(formatTag);
+ Assert.IsNotNull(formats);
+ foreach (AcmFormat format in formats)
+ {
+ Debug.WriteLine(String.Format("{0} {1} {2} {3} {4}",
+ format.FormatIndex,
+ format.FormatTag,
+ format.FormatDescription,
+ format.WaveFormat,
+ format.SupportFlags));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/NAudioTests/Acm/GsmEncodeTest.cs b/NAudioTests/Acm/GsmEncodeTest.cs
new file mode 100644
index 00000000..0e8e1e1e
--- /dev/null
+++ b/NAudioTests/Acm/GsmEncodeTest.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+using System.IO;
+
+namespace NAudioTests.Acm
+{
+ [TestFixture]
+ public class GsmEncodeTest
+ {
+ [Test]
+ public void CanEncodeGsm()
+ {
+ var testFile = @"C:\Users\Mark\Code\CodePlex\AudioTestFiles\WAV\PCM 16 bit\pcm mono 16 bit 8kHz.wav";
+ if (!File.Exists(testFile))
+ {
+ Assert.Ignore("Missing test file");
+ }
+ using (var reader = new WaveFileReader(testFile))
+ {
+ using (var gsm = new WaveFormatConversionStream(new Gsm610WaveFormat(), reader))
+ {
+ WaveFileWriter.CreateWaveFile(@"C:\Users\Mark\Code\CodePlex\gsm.wav", gsm);
+ }
+ }
+ }
+
+ [Test]
+ public void CanDecodeGsm()
+ {
+ var testFile = @"C:\Users\Mark\Code\CodePlex\gsm.wav";
+ if (!File.Exists(testFile))
+ {
+ Assert.Ignore("Missing test file");
+ }
+ using (var reader = new WaveFileReader(testFile))
+ {
+ using (var pcm = WaveFormatConversionStream.CreatePcmStream(reader))
+ {
+ WaveFileWriter.CreateWaveFile(@"C:\Users\Mark\Code\CodePlex\gsm-decoded.wav", pcm);
+ }
+ }
+ }
+ }
+}
diff --git a/NAudioTests/Acm/WaveFormatConversionStreamTests.cs b/NAudioTests/Acm/WaveFormatConversionStreamTests.cs
new file mode 100644
index 00000000..c6762c23
--- /dev/null
+++ b/NAudioTests/Acm/WaveFormatConversionStreamTests.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+using NAudio.Wave.Compression;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using NAudioTests.Utils;
+
+namespace NAudioTests.Acm
+{
+ [TestFixture]
+ [Category("IntegrationTest")]
+ public class WaveFormatConversionStreamTests
+ {
+ [Test]
+ public void CanConvertPcmToMuLaw()
+ {
+ int channels = 1;
+ int sampleRate = 8000;
+ CanCreateConversionStream(
+ new WaveFormat(sampleRate, 16, channels),
+ WaveFormat.CreateCustomFormat(WaveFormatEncoding.MuLaw, sampleRate, channels, sampleRate * channels, 1, 8));
+ }
+
+ [Test]
+ public void CanConvertPcmToALaw()
+ {
+ int channels = 1;
+ int sampleRate = 8000;
+ CanCreateConversionStream(
+ new WaveFormat(sampleRate, 16, channels),
+ WaveFormat.CreateCustomFormat(WaveFormatEncoding.ALaw, sampleRate, channels, sampleRate * channels, 1, 8));
+ }
+
+ /* Windows does not provide an ACM MP3 encoder, but this test could be run
+ * if you install a different ACM MP3 encoder to see if the MP3 Wave Format
+ * NAudio creates is sufficient (possibly it will have its own custom metadata
+ * in the WaveFormat extra byts).
+ [Test]
+ public void CanConvertPcmToMp3()
+ {
+ int channels = 2;
+ int sampleRate = 44100;
+ CanCreateConversionStream(
+ new WaveFormat(sampleRate, 16, channels),
+ new Mp3WaveFormat(sampleRate, channels, 0, 128000/8));
+ }*/
+
+ [Test]
+ public void CanConvertALawToPcm()
+ {
+ int channels = 1;
+ int sampleRate = 8000;
+ CanCreateConversionStream(
+ WaveFormat.CreateCustomFormat(WaveFormatEncoding.ALaw, sampleRate, channels, sampleRate * channels, 1, 8),
+ new WaveFormat(sampleRate, 16, channels));
+ }
+
+ [Test]
+ public void CanConvertMuLawToPcm()
+ {
+ int channels = 1;
+ int sampleRate = 8000;
+ CanCreateConversionStream(
+ WaveFormat.CreateCustomFormat(WaveFormatEncoding.MuLaw, sampleRate, channels, sampleRate * channels, 1, 8),
+ new WaveFormat(sampleRate, 16, channels));
+ }
+
+ [Test]
+ public void CanConvertAdpcmToPcm()
+ {
+ int channels = 1;
+ int sampleRate = 8000;
+ CanCreateConversionStream(
+ new AdpcmWaveFormat(8000,1),
+ new WaveFormat(sampleRate, 16, channels));
+ }
+
+ [Test]
+ public void CanConvertAdpcmToSuggestedPcm()
+ {
+ using(WaveStream stream = WaveFormatConversionStream.CreatePcmStream(
+ new NullWaveStream(new AdpcmWaveFormat(8000, 1),1000)))
+ {
+ }
+ }
+
+ [Test]
+ public void CanConvertALawToSuggestedPcm()
+ {
+ using (WaveStream stream = WaveFormatConversionStream.CreatePcmStream(
+ new NullWaveStream(WaveFormat.CreateALawFormat(8000,1),1000)))
+ {
+ }
+ }
+
+ [Test]
+ public void CanConvertMuLawToSuggestedPcm()
+ {
+ using (WaveStream stream = WaveFormatConversionStream.CreatePcmStream(
+ new NullWaveStream(WaveFormat.CreateMuLawFormat(8000, 1), 1000)))
+ {
+ }
+ }
+
+ [Test]
+ public void CanConvertPcmToAdpcm()
+ {
+ int channels = 1;
+ int sampleRate = 8000;
+ CanCreateConversionStream(
+ new WaveFormat(sampleRate, 16, channels),
+ new AdpcmWaveFormat(8000, 1));
+ }
+
+ [Test]
+ public void CanConvertImeAdpcmToPcm()
+ {
+ AcmDriver driver = AcmDriver.FindByShortName("Microsoft IMA ADPCM");
+ driver.Open();
+ try
+ {
+ foreach (AcmFormatTag formatTag in driver.FormatTags)
+ {
+ foreach (AcmFormat format in driver.GetFormats(formatTag))
+ {
+ if (format.FormatTag == WaveFormatEncoding.DviAdpcm ||
+ format.FormatTag == WaveFormatEncoding.ImaAdpcm)
+ {
+ // see if we can convert it to 16 bit PCM
+ Debug.WriteLine(String.Format("Converting {0} to PCM", format.WaveFormat));
+ CanCreateConversionStream(format.WaveFormat,
+ new WaveFormat(format.WaveFormat.SampleRate, 16, format.WaveFormat.Channels));
+ }
+ }
+ }
+ }
+ finally
+ {
+ driver.Close();
+ }
+ }
+
+ private void CanCreateConversionStream(WaveFormat inputFormat, WaveFormat outputFormat)
+ {
+ WaveStream inputStream = new NullWaveStream(inputFormat, 10000);
+ using (WaveFormatConversionStream stream = new WaveFormatConversionStream(
+ outputFormat, inputStream))
+ {
+ byte[] buffer = new byte[stream.WaveFormat.AverageBytesPerSecond];
+ int totalRead = 0;
+ int bytesRead;
+ do
+ {
+ bytesRead = stream.Read(buffer, 0, buffer.Length);
+ totalRead += bytesRead;
+ } while (bytesRead > 0);
+ Debug.WriteLine(String.Format("Converted {0}", totalRead));
+ Assert.AreEqual(inputStream.Length, inputStream.Position);
+ }
+ }
+ }
+
+
+}
+
diff --git a/NAudioTests/Aiff/AiffReaderTests.cs b/NAudioTests/Aiff/AiffReaderTests.cs
new file mode 100644
index 00000000..070995da
--- /dev/null
+++ b/NAudioTests/Aiff/AiffReaderTests.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using System.IO;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudioTests.Aiff
+{
+ [TestFixture]
+ public class AiffReaderTests
+ {
+ [Test]
+ [Category("IntegrationTest")]
+ public void ConvertAiffToWav()
+ {
+ string testFolder = @"C:\Users\Mark\Downloads\NAudio";
+ if (!Directory.Exists(testFolder))
+ {
+ Assert.Ignore("{0} not found", testFolder);
+ }
+
+ foreach (string file in Directory.GetFiles(testFolder, "*.aiff"))
+ {
+ string baseName= Path.GetFileNameWithoutExtension(file);
+ string wavFile = Path.Combine(testFolder, baseName + ".wav");
+ string aiffFile = Path.Combine(testFolder, file);
+ Debug.WriteLine(String.Format("Converting {0} to wav", aiffFile));
+ ConvertAiffToWav(aiffFile, wavFile);
+ }
+ }
+
+ private static void ConvertAiffToWav(string aiffFile, string wavFile)
+ {
+ using (var reader = new AiffFileReader(aiffFile))
+ {
+ using (var writer = new WaveFileWriter(wavFile, reader.WaveFormat))
+ {
+ byte[] buffer = new byte[4096];
+ int bytesRead = 0;
+ do
+ {
+ bytesRead = reader.Read(buffer, 0, buffer.Length);
+ writer.Write(buffer, 0, bytesRead);
+ } while (bytesRead > 0);
+ }
+ }
+ }
+ }
+}
diff --git a/NAudioTests/DirectSound/DirectSoundTests.cs b/NAudioTests/DirectSound/DirectSoundTests.cs
new file mode 100644
index 00000000..604de64b
--- /dev/null
+++ b/NAudioTests/DirectSound/DirectSoundTests.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudioTests.DirectSound
+{
+ [TestFixture]
+ public class DirectSoundTests
+ {
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanEnumerateDevices()
+ {
+ foreach(var device in DirectSoundOut.Devices)
+ {
+ Debug.WriteLine(String.Format("{0} {1} {2}", device.Description, device.ModuleName, device.Guid));
+ }
+ }
+ }
+}
diff --git a/NAudioTests/Dmo/DmoMp3FrameDecompressorTests.cs b/NAudioTests/Dmo/DmoMp3FrameDecompressorTests.cs
new file mode 100644
index 00000000..d0070a74
--- /dev/null
+++ b/NAudioTests/Dmo/DmoMp3FrameDecompressorTests.cs
@@ -0,0 +1,113 @@
+using System;
+using NUnit.Framework;
+using NAudio.FileFormats.Mp3;
+using NAudio.Wave;
+using NAudio.Dmo;
+using System.Diagnostics;
+using System.IO;
+using NAudioTests.Utils;
+
+namespace NAudioTests.Dmo
+{
+ [TestFixture]
+ public class DmoMp3FrameDecompressorTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ OSUtils.RequireVista();
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanCreateDmoMp3FrameDecompressor()
+ {
+ var mp3Format = new Mp3WaveFormat(44100,2,215,32000);
+ var frameDecompressor = new DmoMp3FrameDecompressor(mp3Format);
+ Assert.IsNotNull(frameDecompressor);
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanDecompressAnMp3()
+ {
+ var testFile = @"C:\Users\Public\Music\Coldplay\X&Y\01-Square One.mp3";
+ if (!File.Exists(testFile))
+ {
+ Assert.Ignore("{0} not found", testFile);
+ }
+ using(var reader = new Mp3FileReader(testFile))
+ {
+ var frameDecompressor = new DmoMp3FrameDecompressor(reader.Mp3WaveFormat);
+ Mp3Frame frame = null;
+ var buffer = new byte[reader.WaveFormat.AverageBytesPerSecond];
+ while ((frame = reader.ReadNextFrame()) != null)
+ {
+ int decompressed = frameDecompressor.DecompressFrame(frame, buffer, 0);
+ Debug.WriteLine(String.Format("Decompressed {0} bytes to {1}", frame.FrameLength, decompressed));
+ }
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanExamineInputTypesOnMp3Decoder()
+ {
+ var decoder = new WindowsMediaMp3Decoder();
+ Assert.AreEqual(decoder.MediaObject.InputStreamCount, 1);
+ foreach (DmoMediaType mediaType in decoder.MediaObject.GetInputTypes(0))
+ {
+ Debug.WriteLine(String.Format("{0}:{1}:{2}",
+ mediaType.MajorTypeName,
+ mediaType.SubTypeName,
+ mediaType.FormatTypeName));
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanExamineOutputTypesOnDecoder()
+ {
+ var decoder = new WindowsMediaMp3Decoder();
+ decoder.MediaObject.SetInputWaveFormat(0,new Mp3WaveFormat(44100, 2, 200, 32000));
+ Assert.AreEqual(decoder.MediaObject.OutputStreamCount, 1);
+
+ foreach (DmoMediaType mediaType in decoder.MediaObject.GetOutputTypes(0))
+ {
+ Debug.WriteLine(String.Format("{0}:{1}:{2}",
+ mediaType.MajorTypeName,
+ mediaType.SubTypeName,
+ mediaType.FormatTypeName));
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void WindowsMediaMp3DecoderSupportsStereoMp3()
+ {
+ WaveFormat waveFormat = new Mp3WaveFormat(44100, 2, 0, 32000);
+ Assert.IsTrue(IsInputFormatSupported(waveFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ [Ignore("Doesn't seem to be true anymore")]
+ public void WindowsMediaMp3DecoderSupportsPcmOutput()
+ {
+ var waveFormat = new WaveFormat(44100, 2);
+ Assert.IsTrue(IsOutputFormatSupported(waveFormat));
+ }
+
+ private bool IsInputFormatSupported(WaveFormat waveFormat)
+ {
+ var decoder = new WindowsMediaMp3Decoder();
+ return decoder.MediaObject.SupportsInputWaveFormat(0, waveFormat);
+ }
+
+ private bool IsOutputFormatSupported(WaveFormat waveFormat)
+ {
+ var decoder = new WindowsMediaMp3Decoder();
+ return decoder.MediaObject.SupportsOutputWaveFormat(0, waveFormat);
+ }
+ }
+}
diff --git a/NAudioTests/Dmo/DmoTests.cs b/NAudioTests/Dmo/DmoTests.cs
new file mode 100644
index 00000000..23491337
--- /dev/null
+++ b/NAudioTests/Dmo/DmoTests.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Dmo;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudioTests.Dmo
+{
+ [TestFixture]
+ public class DmoTests
+ {
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanEnumerateAudioEffects()
+ {
+ Debug.WriteLine("Audio Effects:");
+ foreach (var dmo in DmoEnumerator.GetAudioEffectNames())
+ {
+ Debug.WriteLine(string.Format("{0} {1}", dmo.Name, dmo.Clsid));
+ var mediaObject = Activator.CreateInstance(Type.GetTypeFromCLSID(dmo.Clsid));
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanEnumerateAudioEncoders()
+ {
+ Debug.WriteLine("Audio Encoders:");
+ foreach (var dmo in DmoEnumerator.GetAudioEncoderNames())
+ {
+ Debug.WriteLine(string.Format("{0} {1}", dmo.Name, dmo.Clsid));
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanEnumerateAudioDecoders()
+ {
+ Debug.WriteLine("Audio Decoders:");
+ foreach (var dmo in DmoEnumerator.GetAudioDecoderNames())
+ {
+ Debug.WriteLine(string.Format("{0} {1}", dmo.Name, dmo.Clsid));
+ }
+ }
+ }
+}
+
diff --git a/NAudioTests/Dmo/ResamplerDmoStreamTests.cs b/NAudioTests/Dmo/ResamplerDmoStreamTests.cs
new file mode 100644
index 00000000..8c3431cc
--- /dev/null
+++ b/NAudioTests/Dmo/ResamplerDmoStreamTests.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+using System.Diagnostics;
+using NAudioTests.Utils;
+
+namespace NAudioTests.Dmo
+{
+ [TestFixture]
+ public class ResamplerDmoStreamTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ OSUtils.RequireVista();
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanCreateResamplerStream()
+ {
+ //using (WaveFileReader reader = new WaveFileReader("C:\\Users\\Mark\\Recording\\REAPER\\ideas-2008-05-17.wav"))
+ using (WaveStream reader = new NullWaveStream(new WaveFormat(44100,16,1),1000 ))
+ {
+ using (ResamplerDmoStream resampler = new ResamplerDmoStream(reader, WaveFormat.CreateIeeeFloatWaveFormat(48000,2)))
+ {
+ Assert.Greater(resampler.Length, reader.Length, "Length");
+ Assert.AreEqual(0, reader.Position, "Position");
+ Assert.AreEqual(0, resampler.Position, "Position");
+ }
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanReadABlockFromResamplerStream()
+ {
+ //using (WaveFileReader reader = new WaveFileReader("C:\\Users\\Mark\\Recording\\REAPER\\ideas-2008-05-17.wav"))
+ WaveFormat inputFormat = new WaveFormat(44100, 16, 1);
+ using (WaveStream reader = new NullWaveStream(inputFormat, inputFormat.AverageBytesPerSecond * 20))
+ {
+ using (ResamplerDmoStream resampler = new ResamplerDmoStream(reader, WaveFormat.CreateIeeeFloatWaveFormat(48000, 2)))
+ {
+ // try to read 10 ms;
+ int bytesToRead = resampler.WaveFormat.AverageBytesPerSecond / 100;
+ byte[] buffer = new byte[bytesToRead];
+ int count = resampler.Read(buffer, 0, bytesToRead);
+ Assert.That(count > 0, "Bytes Read");
+ }
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanResampleAWholeStreamToIEEE()
+ {
+ WaveFormat inputFormat = new WaveFormat(44100, 16, 2);
+ WaveFormat outputFormat = WaveFormat.CreateIeeeFloatWaveFormat(48000, 2);
+ ResampleAWholeStream(inputFormat, outputFormat);
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanResampleAWholeStreamTo48000PCM()
+ {
+ WaveFormat inputFormat = new WaveFormat(44100, 16, 2);
+ WaveFormat outputFormat = new WaveFormat(48000, 16, 2);
+ ResampleAWholeStream(inputFormat, outputFormat);
+ }
+
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanResampleAWholeStreamTo44100IEEE()
+ {
+ WaveFormat inputFormat = new WaveFormat(48000, 16, 2);
+ WaveFormat outputFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
+ ResampleAWholeStream(inputFormat, outputFormat);
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanResampleAWholeStreamTo44100PCM()
+ {
+ WaveFormat inputFormat = new WaveFormat(48000, 16, 2);
+ WaveFormat outputFormat = new WaveFormat(44100, 16, 2);
+ ResampleAWholeStream(inputFormat, outputFormat);
+ }
+
+ private void ResampleAWholeStream(WaveFormat inputFormat, WaveFormat outputFormat)
+ {
+ using (WaveStream reader = new NullWaveStream(inputFormat, inputFormat.AverageBytesPerSecond * 20))
+ {
+ using (ResamplerDmoStream resampler = new ResamplerDmoStream(reader, outputFormat))
+ {
+ // try to read 10 ms;
+ int bytesToRead = resampler.WaveFormat.AverageBytesPerSecond / 100;
+ byte[] buffer = new byte[bytesToRead];
+ int count;
+ int total = 0;
+ do
+ {
+ count = resampler.Read(buffer, 0, bytesToRead);
+ total += count;
+ //Assert.AreEqual(count, bytesToRead, "Bytes Read");
+ } while (count > 0);
+ //Debug.WriteLine(String.Format("Converted input length {0} to {1}", reader.Length, total));
+ }
+ }
+ }
+
+ /*[Test]
+ public void CanResampleToWav()
+ {
+ using (WaveFileReader reader = new WaveFileReader("C:\\Users\\Mark\\Recording\\REAPER\\ideas-2008-05-17.wav"))
+ {
+ using (ResamplerDmoStream resampler = new ResamplerDmoStream(reader, new WaveFormat(48000, 16, 2)))
+ {
+ using (WaveFileWriter writer = new WaveFileWriter("C:\\Users\\Mark\\Recording\\REAPER\\ideas-converted.wav", resampler.WaveFormat))
+ {
+ // try to read 10 ms;
+ int bytesToRead = resampler.WaveFormat.AverageBytesPerSecond / 100;
+ byte[] buffer = new byte[bytesToRead];
+ int count;
+ int total = 0;
+ do
+ {
+ count = resampler.Read(buffer, 0, bytesToRead);
+ writer.WriteData(buffer, 0, count);
+ total += count;
+ //Assert.AreEqual(count, bytesToRead, "Bytes Read");
+ } while (count > 0);
+ Debug.WriteLine(String.Format("Converted input length {0} to {1}", reader.Length, total));
+ }
+ }
+ }
+ }*/
+ }
+}
diff --git a/NAudioTests/Dmo/ResamplerDmoTests.cs b/NAudioTests/Dmo/ResamplerDmoTests.cs
new file mode 100644
index 00000000..3c92667b
--- /dev/null
+++ b/NAudioTests/Dmo/ResamplerDmoTests.cs
@@ -0,0 +1,201 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Dmo;
+using NAudio.Wave;
+using System.Diagnostics;
+using NAudioTests.Utils;
+
+namespace NAudioTests.Dmo
+{
+ [TestFixture]
+ public class ResamplerDmoTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ OSUtils.RequireVista();
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanCreateResamplerMediaObject()
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanExamineInputTypesOnResampler()
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ Assert.AreEqual(dmoResampler.MediaObject.InputStreamCount, 1);
+ foreach (DmoMediaType mediaType in dmoResampler.MediaObject.GetInputTypes(0))
+ {
+ Debug.WriteLine(String.Format("{0}:{1}:{2}",
+ mediaType.MajorTypeName,
+ mediaType.SubTypeName,
+ mediaType.FormatTypeName));
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanExamineOutputTypesOnResampler()
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ Assert.AreEqual(dmoResampler.MediaObject.OutputStreamCount, 1);
+ foreach (DmoMediaType mediaType in dmoResampler.MediaObject.GetOutputTypes(0))
+ {
+ Debug.WriteLine(String.Format("{0}:{1}:{2}",
+ mediaType.MajorTypeName,
+ mediaType.SubTypeName,
+ mediaType.FormatTypeName));
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerSupports16BitPCM41000Input()
+ {
+ WaveFormat waveFormat = new WaveFormat(44100, 16, 2);
+ Assert.IsTrue(IsResamplerInputFormatSupported(waveFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerSupports16BitPCM8000Input()
+ {
+ WaveFormat waveFormat = new WaveFormat(8000, 16, 2);
+ Assert.IsTrue(IsResamplerInputFormatSupported(waveFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerSupportsIEEE44100Input()
+ {
+ WaveFormat waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
+ Assert.IsTrue(IsResamplerInputFormatSupported(waveFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerSupportsIEEE8000Input()
+ {
+ WaveFormat waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(8000, 2);
+ Assert.IsTrue(IsResamplerInputFormatSupported(waveFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerSupports8000To44100IEEE()
+ {
+ WaveFormat inputFormat = WaveFormat.CreateIeeeFloatWaveFormat(8000, 2);
+ WaveFormat outputFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
+ Assert.IsTrue(IsResamplerConversionSupported(inputFormat, outputFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerSupports41000To48000IEEE()
+ {
+ WaveFormat inputFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
+ WaveFormat outputFormat = WaveFormat.CreateIeeeFloatWaveFormat(48000, 2);
+ Assert.IsTrue(IsResamplerConversionSupported(inputFormat, outputFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerSupportsPCMToIEEE()
+ {
+ WaveFormat inputFormat = new WaveFormat(44100, 16, 2);
+ WaveFormat outputFormat = WaveFormat.CreateIeeeFloatWaveFormat(48000, 2);
+ Assert.IsTrue(IsResamplerConversionSupported(inputFormat, outputFormat));
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerCanGetInputAndOutputBufferSizes()
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ dmoResampler.MediaObject.SetInputWaveFormat(0, WaveFormat.CreateIeeeFloatWaveFormat(44100, 2));
+ dmoResampler.MediaObject.SetOutputWaveFormat(0, WaveFormat.CreateIeeeFloatWaveFormat(48000, 2));
+ MediaObjectSizeInfo inputSizeInfo = dmoResampler.MediaObject.GetInputSizeInfo(0);
+ Assert.IsNotNull(inputSizeInfo, "Input Size Info");
+ Debug.WriteLine(inputSizeInfo.ToString());
+ MediaObjectSizeInfo outputSizeInfo = dmoResampler.MediaObject.GetOutputSizeInfo(0);
+ Assert.IsNotNull(outputSizeInfo, "Output Size Info");
+ Debug.WriteLine(outputSizeInfo.ToString());
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerCanCallProcessInput()
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ dmoResampler.MediaObject.SetInputWaveFormat(0, WaveFormat.CreateIeeeFloatWaveFormat(44100, 2));
+ dmoResampler.MediaObject.SetOutputWaveFormat(0, WaveFormat.CreateIeeeFloatWaveFormat(48000, 2));
+ using (MediaBuffer buffer = new MediaBuffer(44100 * 2 * 4))
+ {
+ buffer.Length = 8000;
+ dmoResampler.MediaObject.ProcessInput(0, buffer, DmoInputDataBufferFlags.None, 0, 0);
+ }
+ }
+
+ [Test]
+ [Category("IntegrationTest")]
+ public void ResamplerCanCallProcessOutput()
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ WaveFormat inputFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
+ WaveFormat outputFormat = WaveFormat.CreateIeeeFloatWaveFormat(48000, 2);
+ dmoResampler.MediaObject.SetInputWaveFormat(0, inputFormat);
+ dmoResampler.MediaObject.SetOutputWaveFormat(0, outputFormat);
+ dmoResampler.MediaObject.AllocateStreamingResources();
+ using (MediaBuffer inputBuffer = new MediaBuffer(inputFormat.AverageBytesPerSecond))
+ {
+ inputBuffer.Length = inputFormat.AverageBytesPerSecond / 10;
+ Debug.WriteLine(String.Format("Input Length {0}", inputBuffer.Length));
+ dmoResampler.MediaObject.ProcessInput(0, inputBuffer, DmoInputDataBufferFlags.None, 0, 0);
+ Debug.WriteLine(String.Format("Input Length {0}", inputBuffer.Length));
+ Debug.WriteLine(String.Format("Input Lookahead {0}", dmoResampler.MediaObject.GetInputSizeInfo(0).MaxLookahead));
+ //Debug.WriteLine(String.Format("Input Max Latency {0}", resampler.MediaObject.GetInputMaxLatency(0)));
+ using (DmoOutputDataBuffer outputBuffer = new DmoOutputDataBuffer(outputFormat.AverageBytesPerSecond))
+ {
+ // one buffer for each output stream
+ dmoResampler.MediaObject.ProcessOutput(DmoProcessOutputFlags.None, 1, new DmoOutputDataBuffer[] { outputBuffer });
+ Debug.WriteLine(String.Format("Converted length: {0}", outputBuffer.Length));
+ Debug.WriteLine(String.Format("Converted flags: {0}", outputBuffer.StatusFlags));
+ //Assert.AreEqual((int)(inputBuffer.Length * 48000.0 / inputFormat.SampleRate), outputBuffer.Length, "Converted buffer length");
+ }
+
+ using (DmoOutputDataBuffer outputBuffer = new DmoOutputDataBuffer(48000 * 2 * 4))
+ {
+ // one buffer for each output stream
+ dmoResampler.MediaObject.ProcessOutput(DmoProcessOutputFlags.None, 1, new DmoOutputDataBuffer[] { outputBuffer });
+ Debug.WriteLine(String.Format("Converted length: {0}", outputBuffer.Length));
+ Debug.WriteLine(String.Format("Converted flags: {0}", outputBuffer.StatusFlags));
+ //Assert.AreEqual((int)(inputBuffer.Length * 48000.0 / inputFormat.SampleRate), outputBuffer.Length, "Converted buffer length");
+ }
+ }
+ dmoResampler.MediaObject.FreeStreamingResources();
+ }
+
+ #region Helper Functions
+ private bool IsResamplerInputFormatSupported(WaveFormat waveFormat)
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ return dmoResampler.MediaObject.SupportsInputWaveFormat(0, waveFormat);
+ }
+
+ private bool IsResamplerConversionSupported(WaveFormat from, WaveFormat to)
+ {
+ DmoResampler dmoResampler = new DmoResampler();
+ // need to set an input format before we can ask for an output format to
+ dmoResampler.MediaObject.SetInputWaveFormat(0, from);
+ return dmoResampler.MediaObject.SupportsOutputWaveFormat(0, to);
+ }
+ #endregion
+ }
+}
diff --git a/NAudioTests/MediaFoundation/MediaFoundationReaderTests.cs b/NAudioTests/MediaFoundation/MediaFoundationReaderTests.cs
new file mode 100644
index 00000000..575b0dc9
--- /dev/null
+++ b/NAudioTests/MediaFoundation/MediaFoundationReaderTests.cs
@@ -0,0 +1,30 @@
+using System;
+using System.IO;
+using System.Linq;
+using NAudio.Wave;
+using NUnit.Framework;
+
+namespace NAudioTests.MediaFoundation
+{
+ [TestFixture]
+ [Category("IntegrationTest")]
+ public class MediaFoundationReaderTests
+ {
+ [Test]
+ public void CanReadAnAac()
+ {
+ var testFile = @"C:\Users\mheath\Downloads\NAudio\AAC\halfspeed.aac";
+ if (!File.Exists(testFile)) Assert.Ignore("Missing test file");
+ var reader = new MediaFoundationReader(testFile);
+ Console.WriteLine(reader.WaveFormat);
+ var buffer = new byte[reader.WaveFormat.AverageBytesPerSecond];
+ int bytesRead;
+ long total = 0;
+ while((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0)
+ {
+ total += bytesRead;
+ }
+ Assert.IsTrue(total > 0);
+ }
+ }
+}
diff --git a/NAudioTests/Midi/MidiEventCollectionTest.cs b/NAudioTests/Midi/MidiEventCollectionTest.cs
new file mode 100644
index 00000000..5fb32998
--- /dev/null
+++ b/NAudioTests/Midi/MidiEventCollectionTest.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Midi;
+
+namespace NAudioTests.Midi
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class MidiEventCollectionTest
+ {
+ [Test]
+ public void TestType1()
+ {
+ MidiEventCollection collection = new MidiEventCollection(1,120);
+ collection.AddEvent(new TextEvent("Test",MetaEventType.TextEvent,0),0);
+ collection.AddEvent(new NoteOnEvent(0, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(15, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(30, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(0, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(15, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(30, 10, 60, 100, 15), 10);
+ Assert.AreEqual(collection.Tracks, 11);
+ collection.PrepareForExport();
+ Assert.AreEqual(collection.Tracks, 3);
+ IList track0 = collection.GetTrackEvents(0);
+ Assert.AreEqual(track0.Count, 2);
+ Assert.AreEqual(collection.GetTrackEvents(1).Count, 4);
+ Assert.AreEqual(collection.GetTrackEvents(2).Count, 4);
+ Assert.IsTrue(MidiEvent.IsEndTrack(track0[track0.Count - 1]));
+ }
+
+ [Test]
+ public void TestType0()
+ {
+ MidiEventCollection collection = new MidiEventCollection(0, 120);
+ collection.AddEvent(new TextEvent("Test", MetaEventType.TextEvent, 0), 0);
+ collection.AddEvent(new NoteOnEvent(0, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(15, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(30, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(0, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(15, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(30, 10, 60, 100, 15), 10);
+ Assert.AreEqual(collection.Tracks, 1);
+ collection.PrepareForExport();
+ Assert.AreEqual(collection.Tracks, 1);
+ IList track0 = collection.GetTrackEvents(0);
+ Assert.AreEqual(track0.Count, 8);
+ Assert.IsTrue(MidiEvent.IsEndTrack(track0[track0.Count - 1]));
+ }
+
+ [Test]
+ public void TestType1ToType0()
+ {
+ MidiEventCollection collection = new MidiEventCollection(1, 120);
+ collection.AddEvent(new TextEvent("Test", MetaEventType.TextEvent, 0), 0);
+ collection.AddEvent(new NoteOnEvent(0, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(15, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(30, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(0, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(15, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(30, 10, 60, 100, 15), 10);
+ Assert.AreEqual(collection.Tracks, 11);
+ collection.MidiFileType = 0;
+ collection.PrepareForExport();
+ Assert.AreEqual(collection.Tracks, 1);
+ IList track0 = collection.GetTrackEvents(0);
+ Assert.AreEqual(track0.Count, 8);
+ Assert.IsTrue(MidiEvent.IsEndTrack(track0[track0.Count - 1]));
+ }
+
+ [Test]
+ public void TestType0ToType1()
+ {
+ MidiEventCollection collection = new MidiEventCollection(0, 120);
+ collection.AddEvent(new TextEvent("Test", MetaEventType.TextEvent, 0), 0);
+ collection.AddEvent(new NoteOnEvent(0, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(15, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(30, 1, 30, 100, 15), 1);
+ collection.AddEvent(new NoteOnEvent(0, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(15, 10, 60, 100, 15), 10);
+ collection.AddEvent(new NoteOnEvent(30, 10, 60, 100, 15), 10);
+ Assert.AreEqual(collection.Tracks, 1);
+ collection.MidiFileType = 1;
+ collection.PrepareForExport();
+ Assert.AreEqual(3, collection.Tracks, "Wrong number of tracks");
+ IList track0 = collection.GetTrackEvents(0);
+ Assert.AreEqual(track0.Count, 2);
+ Assert.AreEqual(collection.GetTrackEvents(1).Count, 4);
+ Assert.AreEqual(collection.GetTrackEvents(2).Count, 4);
+ Assert.IsTrue(MidiEvent.IsEndTrack(track0[track0.Count - 1]));
+ }
+ }
+}
diff --git a/NAudioTests/Midi/PitchWheelChangeEventTests.cs b/NAudioTests/Midi/PitchWheelChangeEventTests.cs
new file mode 100644
index 00000000..9d01c15d
--- /dev/null
+++ b/NAudioTests/Midi/PitchWheelChangeEventTests.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Midi;
+using System.IO;
+
+namespace NAudioTests.Midi
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class PitchWheelChangeEventTests
+ {
+ [Test]
+ public void GetAsShortMessageReturnsCorrectValue()
+ {
+ int channel = 2;
+ int pitch = 0x3FFF; // 0x2000 is the default
+ PitchWheelChangeEvent p = new PitchWheelChangeEvent(0, channel, pitch);
+
+ Assert.AreEqual(0x007F7FE1, p.GetAsShortMessage());
+ }
+
+ [Test]
+ public void ExportsCorrectValue()
+ {
+ var ms = new MemoryStream();
+ var writer = new BinaryWriter(ms);
+
+ int channel = 2;
+ int pitch = 0x207D; // 0x2000 is the default
+ PitchWheelChangeEvent p = new PitchWheelChangeEvent(0, channel, pitch);
+
+ long time = 0;
+ p.Export(ref time, writer);
+
+ Assert.AreEqual(4, ms.Length);
+ byte[] b = ms.GetBuffer();
+ Assert.AreEqual(0x0, b[0]); // event time
+ Assert.AreEqual(0xE1, b[1]);
+ Assert.AreEqual(0x7D, b[2]);
+ Assert.AreEqual(0x40, b[3]);
+ }
+ }
+}
diff --git a/NAudioTests/Mixer/MixerApiTests.cs b/NAudioTests/Mixer/MixerApiTests.cs
new file mode 100644
index 00000000..4fa4f052
--- /dev/null
+++ b/NAudioTests/Mixer/MixerApiTests.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Mixer;
+using System.Diagnostics;
+using NAudio;
+using NAudio.Wave;
+
+namespace NAudioTests
+{
+ [TestFixture]
+ [Category("IntegrationTest")]
+ public class MixerApiTests
+ {
+ [Test]
+ public void CanEnumerateAllMixerControls()
+ {
+ int devices = Mixer.NumberOfDevices;
+ Assert.That(devices > 0, "Expected at least one mixer device");
+ for (int device = 0; device < devices; device++)
+ {
+ ExploreMixerDevice(device);
+ Debug.WriteLine("");
+ }
+ }
+
+ [Test]
+ public void CanFindDefaultWaveIn()
+ {
+ int defaultWaveInMixerId = MixerLine.GetMixerIdForWaveIn(0);
+ Mixer mixer = new Mixer(defaultWaveInMixerId);
+ foreach (MixerLine destination in mixer.Destinations)
+ {
+ Debug.WriteLine(String.Format("DESTINATION: {0} {1} (Type: {2}, Target: {3})",
+ destination.Name, destination.TypeDescription, destination.ComponentType, destination.TargetName));
+
+ if (destination.ComponentType == MixerLineComponentType.DestinationWaveIn)
+ {
+ foreach (MixerLine source in destination.Sources)
+ {
+ Debug.WriteLine(String.Format("{0} {1} (Source: {2}, Target: {3})",
+ source.Name, source.TypeDescription, source.IsSource, source.TargetName));
+ if (source.ComponentType == MixerLineComponentType.SourceMicrophone)
+ {
+ Debug.WriteLine(String.Format("Found the microphone: {0}", source.Name));
+ foreach (MixerControl control in source.Controls)
+ {
+ if (control.ControlType == MixerControlType.Volume)
+ {
+ Debug.WriteLine(String.Format("Volume Found: {0}", control));
+ UnsignedMixerControl umc = (UnsignedMixerControl)control;
+ uint originalValue = umc.Value;
+ umc.Value = umc.MinValue;
+ Assert.AreEqual(umc.MinValue, umc.Value, "Set Minimum Correctly");
+ umc.Value = umc.MaxValue;
+ Assert.AreEqual(umc.MaxValue, umc.Value, "Set Maximum Correctly");
+ umc.Value = umc.MaxValue / 2;
+ Assert.AreEqual(umc.MaxValue / 2, umc.Value, "Set MidPoint Correctly");
+ umc.Value = originalValue;
+ Assert.AreEqual(originalValue, umc.Value, "Set Original Correctly");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ [Test]
+ public void CanGetWaveInMixerLine()
+ {
+ using (var waveIn = new WaveInEvent())
+ {
+ MixerLine line = waveIn.GetMixerLine();
+ //Debug.WriteLine(String.Format("Mic Level {0}", level));
+ }
+ }
+
+ private static void ExploreMixerDevice(int deviceIndex)
+ {
+ Mixer mixer = new Mixer(deviceIndex);
+ Debug.WriteLine(String.Format("Device {0}: {1}",deviceIndex,mixer.Name));
+ Debug.WriteLine("--------------------------------------------");
+ int destinations = mixer.DestinationCount;
+ Assert.That(destinations > 0, "Expected at least one destination");
+ for (int destinationIndex = 0; destinationIndex < destinations; destinationIndex++)
+ {
+ ExploreMixerDestination(mixer, destinationIndex);
+ }
+ }
+
+ private static void ExploreMixerDestination(Mixer mixer, int destinationIndex)
+ {
+ var destination = mixer.GetDestination(destinationIndex);
+ Debug.WriteLine(String.Format("Destination {0}: {1}",
+ destinationIndex, destination));
+ int channels = destination.Channels;
+ foreach (MixerControl control in destination.Controls)
+ {
+ Debug.WriteLine(String.Format("CONTROL: {0}", control));
+ }
+ int sources = destination.SourceCount;
+ for (int sourceIndex = 0; sourceIndex < sources; sourceIndex++)
+ {
+ ExploreMixerSource(destination, sourceIndex);
+ }
+ }
+
+ private static void ExploreMixerSource(MixerLine destinationLine, int sourceIndex)
+ {
+ var sourceLine = destinationLine.GetSource(sourceIndex);
+ Debug.WriteLine(String.Format("Source {0}: {1}",
+ sourceIndex, sourceLine));
+ foreach (MixerControl control in sourceLine.Controls)
+ {
+ Debug.WriteLine(String.Format("CONTROL: {0}", control));
+ }
+ }
+ }
+}
diff --git a/NAudioTests/Mp3/Mp3FileReaderTests.cs b/NAudioTests/Mp3/Mp3FileReaderTests.cs
new file mode 100644
index 00000000..067bc881
--- /dev/null
+++ b/NAudioTests/Mp3/Mp3FileReaderTests.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using System.IO;
+using NAudio.Wave;
+using System.Diagnostics;
+
+namespace NAudioTests.Mp3
+{
+ [TestFixture]
+ public class Mp3FileReaderTests
+ {
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanLoadAndReadVariousProblemMp3Files()
+ {
+ string testDataFolder = @"C:\Users\Mark\Downloads\NAudio";
+ if (!Directory.Exists(testDataFolder))
+ {
+ Assert.Ignore("{0} not found", testDataFolder);
+ }
+ foreach (string file in Directory.GetFiles(testDataFolder, "*.mp3"))
+ {
+ string mp3File = Path.Combine(testDataFolder, file);
+ Debug.WriteLine(String.Format("Opening {0}", mp3File));
+ using (var reader = new Mp3FileReader(mp3File))
+ {
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ int total = 0;
+ do
+ {
+ bytesRead = reader.Read(buffer, 0, buffer.Length);
+ total += bytesRead;
+ } while (bytesRead > 0);
+ Debug.WriteLine(String.Format("Read {0} bytes", total));
+ }
+ }
+ }
+
+ }
+}
diff --git a/NAudioTests/Mp3/Mp3FrameTests.cs b/NAudioTests/Mp3/Mp3FrameTests.cs
new file mode 100644
index 00000000..f87ae0c5
--- /dev/null
+++ b/NAudioTests/Mp3/Mp3FrameTests.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using System.IO;
+using NAudio.Wave;
+
+namespace NAudioTests.Mp3
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class Mp3FrameTests
+ {
+ private const int crcNotPresent = 1;
+ private const int bitRateIndex = 1;
+
+ private readonly byte[] validMp3FrameHeader = new byte[] { 0xff,
+ (byte)(0xe0 + ((int)MpegVersion.Version2 << 3) + ((int)MpegLayer.Layer3 << 1) + crcNotPresent),
+ (byte)(bitRateIndex << 4), 0x00
+ };
+
+ private byte[] constructValidMp3Frame()
+ {
+ byte[] frame = new byte[52];
+ Array.Copy(validMp3FrameHeader, frame, validMp3FrameHeader.Length);
+ return frame;
+ }
+
+ [Test]
+ public void CanParseValidMp3Frame()
+ {
+ MemoryStream ms = new MemoryStream(constructValidMp3Frame());
+ Mp3Frame frame = Mp3Frame.LoadFromStream(ms);
+ Assert.IsNotNull(frame);
+ }
+
+ [TestCase(0)]
+ [TestCase(3)]
+ [TestCase(4)]
+ [TestCase(8)]
+ [TestCase(12)]
+ public void FailsToParseInvalidFrame(int length)
+ {
+ MemoryStream ms = new MemoryStream(new byte[length]);
+ Mp3Frame frame = Mp3Frame.LoadFromStream(ms);
+ Assert.IsNull(frame);
+ }
+
+ [TestCase(1)]
+ [TestCase(2)]
+ [TestCase(3)]
+ [TestCase(4)]
+ public void CanParseMp3FrameOffsetByN(int offset)
+ {
+ byte[] validMp3Frame = constructValidMp3Frame();
+ byte[] offsetBuffer = new byte[offset + validMp3Frame.Length];
+ Array.Copy(validMp3Frame, 0, offsetBuffer, offset, validMp3Frame.Length);
+ MemoryStream ms = new MemoryStream(offsetBuffer);
+ Mp3Frame frame = Mp3Frame.LoadFromStream(ms);
+ Assert.IsNotNull(frame);
+ }
+ }
+}
diff --git a/NAudioTests/NAudioTests.csproj b/NAudioTests/NAudioTests.csproj
new file mode 100644
index 00000000..6351e760
--- /dev/null
+++ b/NAudioTests/NAudioTests.csproj
@@ -0,0 +1,176 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {5080281A-F9A1-403F-85C7-0DFF6839B07B}
+ Library
+ Properties
+ NAudioTests
+ NAudioTests
+
+
+
+
+
+
+
+
+
+
+
+
+ 3.5
+ v3.5
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ AllRules.ruleset
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ AllRules.ruleset
+
+
+
+ ..\packages\Moq.4.0.10827\lib\NET35\Moq.dll
+
+
+ ..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll
+
+
+ ..\packages\NUnit.2.5.10.11092\lib\nunit.mocks.dll
+
+
+ ..\packages\NUnit.2.5.10.11092\lib\pnunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {1868FC77-FD6F-4881-9CF5-DE7451806BFA}
+ NAudio.WindowsMediaFormat
+
+
+ {DA4F02E3-0B5E-42CD-B8D9-5583FA51D66E}
+ NAudio
+
+
+
+
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+
+
+
+ "$(SolutionDir)Tools\nuget" install "$(ProjectDir)packages.config" -o "$(SolutionDir)Packages"
+
+
+
\ No newline at end of file
diff --git a/NAudioTests/NAudioTests.csproj.vspscc b/NAudioTests/NAudioTests.csproj.vspscc
new file mode 100644
index 00000000..feffdeca
--- /dev/null
+++ b/NAudioTests/NAudioTests.csproj.vspscc
@@ -0,0 +1,10 @@
+""
+{
+"FILE_VERSION" = "9237"
+"ENLISTMENT_CHOICE" = "NEVER"
+"PROJECT_FILE_RELATIVE_PATH" = ""
+"NUMBER_OF_EXCLUDED_FILES" = "0"
+"ORIGINAL_PROJECT_FILE_PATH" = ""
+"NUMBER_OF_NESTED_PROJECTS" = "0"
+"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
+}
diff --git a/NAudioTests/NAudioTests.nunit b/NAudioTests/NAudioTests.nunit
new file mode 100644
index 00000000..9debb769
--- /dev/null
+++ b/NAudioTests/NAudioTests.nunit
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NAudioTests/Properties/AssemblyInfo.cs b/NAudioTests/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..de749918
--- /dev/null
+++ b/NAudioTests/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("NAudioTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("NAudioTests")]
+[assembly: AssemblyCopyright("Copyright © 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1a79f004-58c2-4e72-9503-5bba3d36f0f4")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/NAudioTests/StopwatchExtensions.cs b/NAudioTests/StopwatchExtensions.cs
new file mode 100644
index 00000000..b8a3614d
--- /dev/null
+++ b/NAudioTests/StopwatchExtensions.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+
+namespace System.Diagnostics
+{
+ public static class StopwatchExtensions
+ {
+ public static long Time(this Stopwatch sw, Action action, int iterations)
+ {
+ sw.Reset();
+ sw.Start();
+ for (int i = 0; i < iterations; i++)
+ {
+ action();
+ }
+ sw.Stop();
+
+ return sw.ElapsedMilliseconds;
+ }
+
+ public static long Time(this Stopwatch sw, Action action)
+ {
+ return Time(sw, action, 1);
+ }
+ }
+}
diff --git a/NAudioTests/Utils/BlockAlignedWaveStream.cs b/NAudioTests/Utils/BlockAlignedWaveStream.cs
new file mode 100644
index 00000000..8127a4ab
--- /dev/null
+++ b/NAudioTests/Utils/BlockAlignedWaveStream.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudioTests.Utils
+{
+ class BlockAlignedWaveStream : WaveStream
+ {
+ long position;
+ long length;
+ WaveFormat waveFormat;
+ public BlockAlignedWaveStream(int blockAlignment, long length)
+ {
+ waveFormat = WaveFormat.CreateCustomFormat(WaveFormatEncoding.Pcm, 8000, 1, 16000, blockAlignment, 16);
+ this.length = length;
+ }
+
+ public override int BlockAlign
+ {
+ get
+ {
+ return waveFormat.BlockAlign;
+ }
+ }
+
+ public override WaveFormat WaveFormat
+ {
+ get { return waveFormat; }
+ }
+
+ public override long Length
+ {
+ get { return length; }
+ }
+
+ public override long Position
+ {
+ get
+ {
+ return position;
+ }
+ set
+ {
+ if (position % BlockAlign != 0)
+ {
+ throw new ArgumentException("Must position block aligned");
+ }
+ position = value;
+ }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (count % BlockAlign != 0)
+ {
+ throw new ArgumentException("Must read block aligned");
+ }
+ if (count > length - position)
+ {
+ count = (int)(length - position);
+ }
+ for (int n = 0; n < count; n++)
+ {
+ buffer[n + offset] = (byte) ((position + n) % 256);
+ }
+ position += count;
+ return count;
+ }
+ }
+}
diff --git a/NAudioTests/Utils/ByteEncodingTests.cs b/NAudioTests/Utils/ByteEncodingTests.cs
new file mode 100644
index 00000000..ee64fdd7
--- /dev/null
+++ b/NAudioTests/Utils/ByteEncodingTests.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Utils;
+using NUnit.Framework;
+
+namespace NAudioTests.Utils
+{
+ [TestFixture]
+ public class ByteEncodingTests
+ {
+ [Test]
+ public void CanDecodeString()
+ {
+ var b = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', };
+ Assert.AreEqual("Hello", ByteEncoding.Instance.GetString(b));
+ }
+
+ [Test]
+ public void CanTruncate()
+ {
+ var b = new byte[] {(byte) 'H', (byte) 'e', (byte) 'l', (byte) 'l', (byte) 'o', 0};
+ Assert.AreEqual("Hello", ByteEncoding.Instance.GetString(b));
+ }
+
+ [Test]
+ public void CanTruncateWithThreeParamOverride()
+ {
+ var b = new byte[] { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o', 0 };
+ Assert.AreEqual("Hello", ByteEncoding.Instance.GetString(b,0,b.Length));
+ }
+ }
+}
diff --git a/NAudioTests/Utils/NullWaveStream.cs b/NAudioTests/Utils/NullWaveStream.cs
new file mode 100644
index 00000000..64564162
--- /dev/null
+++ b/NAudioTests/Utils/NullWaveStream.cs
@@ -0,0 +1,46 @@
+using System;
+using NAudio.Wave;
+
+namespace NAudioTests.Utils
+{
+ class NullWaveStream : WaveStream
+ {
+ private readonly WaveFormat format;
+ private readonly long length;
+ private long position;
+
+ public NullWaveStream(WaveFormat format, long length)
+ {
+ this.format = format;
+ this.length = length;
+ }
+
+ public override WaveFormat WaveFormat
+ {
+ get { return format; }
+ }
+
+ public override long Length
+ {
+ get { return length; }
+ }
+
+ public override long Position
+ {
+ get { return position; }
+ set { position = value; }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ if (position > length)
+ {
+ return 0;
+ }
+ count = (int)Math.Min(count, length - position);
+ Array.Clear(buffer, offset, count);
+ position += count;
+ return count;
+ }
+ }
+}
diff --git a/NAudioTests/Utils/OSUtils.cs b/NAudioTests/Utils/OSUtils.cs
new file mode 100644
index 00000000..cb8d56af
--- /dev/null
+++ b/NAudioTests/Utils/OSUtils.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+
+namespace NAudioTests.Utils
+{
+ static class OSUtils
+ {
+ public static void RequireVista()
+ {
+ if (Environment.OSVersion.Version.Major < 6)
+ {
+ Assert.Ignore("This test requires Windows Vista or newer");
+ }
+ }
+
+ public static void RequireXP()
+ {
+ if (Environment.OSVersion.Version.Major >= 6)
+ {
+ Assert.Ignore("This test requires Windows XP");
+ }
+ }
+ }
+}
diff --git a/NAudioTests/Utils/SampleProviderTestHelpers.cs b/NAudioTests/Utils/SampleProviderTestHelpers.cs
new file mode 100644
index 00000000..f8cdd44d
--- /dev/null
+++ b/NAudioTests/Utils/SampleProviderTestHelpers.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+
+namespace NAudioTests.Utils
+{
+ public static class SampleProviderTestHelpers
+ {
+ public static void AssertReadsExpected(this ISampleProvider sampleProvider, float[] expected)
+ {
+ AssertReadsExpected(sampleProvider, expected, expected.Length);
+ }
+
+ public static void AssertReadsExpected(this ISampleProvider sampleProvider, float[] expected, int readSize)
+ {
+ var buffer = new float[readSize];
+ var read = sampleProvider.Read(buffer, 0, readSize);
+ Assert.AreEqual(expected.Length, read, "Number of samples read");
+ for (int n = 0; n < read; n++)
+ {
+ Assert.AreEqual(expected[n], buffer[n], String.Format("Buffer at index {0}",n));
+ }
+ }
+ }
+}
diff --git a/NAudioTests/Wasapi/AudioClientTests.cs b/NAudioTests/Wasapi/AudioClientTests.cs
new file mode 100644
index 00000000..7e25185e
--- /dev/null
+++ b/NAudioTests/Wasapi/AudioClientTests.cs
@@ -0,0 +1,227 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using NUnit.Framework;
+using NAudio.CoreAudioApi;
+using NAudio.Wave;
+using System.Diagnostics;
+using NAudioTests.Utils;
+
+namespace NAudioTests.Wasapi
+{
+ [TestFixture]
+ [Category("IntegrationTest")]
+ public class AudioClientTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ OSUtils.RequireVista();
+ }
+
+ [Test]
+ public void CanGetMixFormat()
+ {
+ // don't need to initialize before asking for MixFormat
+ Debug.WriteLine(String.Format("Mix Format: {0}", GetAudioClient().MixFormat));
+ }
+
+ [Test]
+ public void CanInitializeInSharedMode()
+ {
+ InitializeClient(AudioClientShareMode.Shared);
+ }
+
+ [Test]
+ public void CanInitializeInExclusiveMode()
+ {
+ using (AudioClient audioClient = GetAudioClient())
+ {
+ WaveFormat waveFormat = new WaveFormat(44100, 16, 2); //audioClient.MixFormat;
+ long refTimesPerSecond = 10000000;
+ audioClient.Initialize(AudioClientShareMode.Exclusive,
+ AudioClientStreamFlags.None,
+ refTimesPerSecond / 10,
+ 0,
+ waveFormat,
+ Guid.Empty);
+ }
+ }
+
+ [Test]
+ public void CanGetAudioRenderClient()
+ {
+ Assert.IsNotNull(InitializeClient(AudioClientShareMode.Shared).AudioRenderClient);
+ }
+
+
+ [Test]
+ public void CanGetBufferSize()
+ {
+ Debug.WriteLine(String.Format("Buffer Size: {0}", InitializeClient(AudioClientShareMode.Shared).BufferSize));
+ }
+
+ [Test]
+ public void CanGetCurrentPadding()
+ {
+ Debug.WriteLine(String.Format("CurrentPadding: {0}", InitializeClient(AudioClientShareMode.Shared).CurrentPadding));
+ }
+
+ [Test]
+ public void CanGetDefaultDevicePeriod()
+ {
+ // should not need initialization
+ Debug.WriteLine(String.Format("DefaultDevicePeriod: {0}", GetAudioClient().DefaultDevicePeriod));
+ }
+
+ [Test]
+ public void CanGetMinimumDevicePeriod()
+ {
+ // should not need initialization
+ Debug.WriteLine(String.Format("MinimumDevicePeriod: {0}", GetAudioClient().MinimumDevicePeriod));
+ }
+
+ [Test]
+ public void DefaultFormatIsSupportedInSharedMode()
+ {
+ AudioClient client = GetAudioClient();
+ WaveFormat defaultFormat = client.MixFormat;
+ Assert.IsTrue(client.IsFormatSupported(AudioClientShareMode.Shared, defaultFormat), "Is Format Supported");
+ }
+
+ /* strange as this may seem, WASAPI doesn't seem to like the default format in exclusive mode
+ * it prefers 16 bit (presumably 24 bit on some devices)
+ [Test]
+ public void DefaultFormatIsSupportedInExclusiveMode()
+ {
+ AudioClient client = GetAudioClient();
+ WaveFormat defaultFormat = client.MixFormat;
+ Assert.IsTrue(client.IsFormatSupported(AudioClientShareMode.Exclusive, defaultFormat), "Is Format Supported");
+ }*/
+
+
+ [Test]
+ public void CanRequestIfFormatIsSupportedExtensible44100SharedMode()
+ {
+ WaveFormatExtensible desiredFormat = new WaveFormatExtensible(44100, 32, 2);
+ Debug.WriteLine(desiredFormat);
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, desiredFormat);
+ }
+
+ [Test]
+ public void CanRequestIfFormatIsSupportedExtensible44100ExclusiveMode()
+ {
+ WaveFormatExtensible desiredFormat = new WaveFormatExtensible(44100, 32, 2);
+ Debug.WriteLine(desiredFormat);
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Exclusive, desiredFormat);
+ }
+
+ [Test]
+ public void CanRequestIfFormatIsSupportedExtensible48000()
+ {
+ WaveFormatExtensible desiredFormat = new WaveFormatExtensible(48000, 32, 2);
+ Debug.WriteLine(desiredFormat);
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, desiredFormat);
+ }
+
+ [Test]
+ public void CanRequestIfFormatIsSupportedExtensible48000_16bit()
+ {
+ WaveFormatExtensible desiredFormat = new WaveFormatExtensible(48000, 16, 2);
+ Debug.WriteLine(desiredFormat);
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, desiredFormat);
+ }
+
+ [Test]
+ public void CanRequestIfFormatIsSupportedPCMStereo()
+ {
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, new WaveFormat(44100, 16, 2));
+ }
+
+ [Test]
+ public void CanRequestIfFormatIsSupported8KHzMono()
+ {
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, new WaveFormat(8000, 16, 1));
+ }
+
+ [Test]
+ public void CanRequest48kHz16BitStereo()
+ {
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, new WaveFormat(48000, 16, 2));
+
+ }
+
+ [Test]
+ public void CanRequest48kHz16BitMono()
+ {
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, new WaveFormat(48000, 16, 1));
+ }
+
+ [Test]
+ public void CanRequestIfFormatIsSupportedIeee()
+ {
+ GetAudioClient().IsFormatSupported(AudioClientShareMode.Shared, WaveFormat.CreateIeeeFloatWaveFormat(44100, 2));
+ }
+
+ [Test]
+ public void CanPopulateABuffer()
+ {
+ AudioClient audioClient = InitializeClient(AudioClientShareMode.Shared);
+ AudioRenderClient renderClient = audioClient.AudioRenderClient;
+ int bufferFrameCount = audioClient.BufferSize;
+ IntPtr buffer = renderClient.GetBuffer(bufferFrameCount);
+ // TODO put some stuff in
+ // will tell it it has a silent buffer
+ renderClient.ReleaseBuffer(bufferFrameCount, AudioClientBufferFlags.Silent);
+ }
+
+ [Test, MaxTime(2000)]
+ public void CanCaptureDefaultDeviceInDefaultFormatUsingWasapiCapture()
+ {
+ using (var wasapiClient = new WasapiCapture())
+ {
+ wasapiClient.StartRecording();
+ Thread.Sleep(1000);
+ wasapiClient.StopRecording();
+ }
+ }
+
+ [Test, MaxTime(3000)]
+ public void CanReuseWasapiCapture()
+ {
+ using (var wasapiClient = new WasapiCapture())
+ {
+ wasapiClient.StartRecording();
+ Thread.Sleep(1000);
+ wasapiClient.StopRecording();
+ Thread.Sleep(1000);
+ wasapiClient.StartRecording();
+ }
+ }
+
+ private AudioClient InitializeClient(AudioClientShareMode shareMode)
+ {
+ AudioClient audioClient = GetAudioClient();
+ WaveFormat waveFormat = audioClient.MixFormat;
+ long refTimesPerSecond = 10000000;
+ audioClient.Initialize(shareMode,
+ AudioClientStreamFlags.None,
+ refTimesPerSecond,
+ 0,
+ waveFormat,
+ Guid.Empty);
+ return audioClient;
+ }
+
+ private AudioClient GetAudioClient()
+ {
+ MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
+ MMDevice defaultAudioEndpoint = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
+ AudioClient audioClient = defaultAudioEndpoint.AudioClient;
+ Assert.IsNotNull(audioClient);
+ return audioClient;
+ }
+
+ }
+}
diff --git a/NAudioTests/Wasapi/MMDeviceEnumeratorTests.cs b/NAudioTests/Wasapi/MMDeviceEnumeratorTests.cs
new file mode 100644
index 00000000..c56d0235
--- /dev/null
+++ b/NAudioTests/Wasapi/MMDeviceEnumeratorTests.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.CoreAudioApi;
+using System.Diagnostics;
+using NAudioTests.Utils;
+
+namespace NAudioTests.Wasapi
+{
+ [TestFixture]
+ [Category("IntegrationTest")]
+ public class MMDeviceEnumeratorTests
+ {
+ [Test]
+ public void CanCreateMMDeviceEnumeratorInVista()
+ {
+ OSUtils.RequireVista();
+ MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
+ }
+
+ [Test]
+ public void CanEnumerateDevicesInVista()
+ {
+ OSUtils.RequireVista();
+ MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
+ foreach (MMDevice devices in enumerator.EnumerateAudioEndPoints(DataFlow.All,DeviceState.All))
+ {
+ Debug.WriteLine(devices);
+ }
+ }
+
+ [Test]
+ public void CanEnumerateCaptureDevices()
+ {
+ OSUtils.RequireVista();
+ MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
+ foreach (MMDevice device in enumerator.EnumerateAudioEndPoints(DataFlow.Capture, DeviceState.All))
+ {
+ Debug.WriteLine(String.Format("{0}, {1}", device.FriendlyName, device.State));
+ }
+ }
+
+ [Test]
+ public void CanGetDefaultAudioEndpoint()
+ {
+ OSUtils.RequireVista();
+ MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
+ MMDevice defaultAudioEndpoint = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
+ Assert.IsNotNull(defaultAudioEndpoint);
+ }
+
+ [Test]
+ public void CanActivateDefaultAudioEndpoint()
+ {
+ OSUtils.RequireVista();
+ MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
+ MMDevice defaultAudioEndpoint = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
+ AudioClient audioClient = defaultAudioEndpoint.AudioClient;
+ Assert.IsNotNull(audioClient);
+ }
+
+ [Test]
+ public void ThrowsNotSupportedExceptionInXP()
+ {
+ OSUtils.RequireXP();
+ Assert.Throws(() => new MMDeviceEnumerator());
+ }
+
+ [Test]
+ public void CanGetAudioClockClient()
+ {
+ OSUtils.RequireVista();
+ var enumerator = new MMDeviceEnumerator();
+
+ var captureClient = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console).AudioClient;
+
+ var REFTIMES_PER_MILLISEC = 10000;
+
+ captureClient.Initialize(AudioClientShareMode.Shared, AudioClientStreamFlags.None,
+ REFTIMES_PER_MILLISEC * 100, 0, captureClient.MixFormat, Guid.Empty);
+
+ // get AUDCLNT_E_NOT_INITIALIZED if not init
+
+ var clock = captureClient.AudioClockClient;
+ Console.WriteLine("Clock Frequency: {0}",clock.Frequency);
+ ulong p;
+ ulong qpc;
+ clock.GetPosition(out p, out qpc);
+ Console.WriteLine("Clock Position: {0}:{1}",p,qpc );
+ Console.WriteLine("Adjusted Position: {0}", clock.AdjustedPosition);
+ Console.WriteLine("Can Adjust Position: {0}", clock.CanAdjustPosition);
+ Console.WriteLine("Characteristics: {0}", clock.Characteristics);
+ captureClient.Dispose();
+ }
+ }
+}
diff --git a/NAudioTests/WaveFormats/AdpcmWaveFormatTests.cs b/NAudioTests/WaveFormats/AdpcmWaveFormatTests.cs
new file mode 100644
index 00000000..705173ac
--- /dev/null
+++ b/NAudioTests/WaveFormats/AdpcmWaveFormatTests.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using System.Runtime.InteropServices;
+using NAudio.Wave;
+
+namespace NAudioTests.WaveFormats
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class AdpcmWaveFormatTests
+ {
+ [Test]
+ public void StructureSizeIsCorrect()
+ {
+ WaveFormat waveFormat = new WaveFormat(8000, 16, 1);
+ Assert.AreEqual(18, Marshal.SizeOf(waveFormat), "WaveFormat Size");
+ AdpcmWaveFormat adpcmWaveFormat = new AdpcmWaveFormat(8000,1);
+ Assert.AreEqual(18 + 32, Marshal.SizeOf(adpcmWaveFormat), "WaveFormat Size");
+ }
+
+ [Test]
+ public void StructureContentsAreCorrect()
+ {
+ AdpcmWaveFormat adpcmWaveFormat = new AdpcmWaveFormat(8000,1);
+ Assert.AreEqual(WaveFormatEncoding.Adpcm, adpcmWaveFormat.Encoding, "Encoding");
+ Assert.AreEqual(8000, adpcmWaveFormat.SampleRate, "Sample Rate");
+ Assert.AreEqual(1, adpcmWaveFormat.Channels, "Channels");
+ Assert.AreEqual(4, adpcmWaveFormat.BitsPerSample, "Bits Per Sample");
+ Assert.AreEqual(4096, adpcmWaveFormat.AverageBytesPerSecond, "Average Bytes Per Second");
+ Assert.AreEqual(32, adpcmWaveFormat.ExtraSize, "Extra Size");
+ Assert.AreEqual(256, adpcmWaveFormat.BlockAlign, "Block Align");
+ Assert.AreEqual(500, adpcmWaveFormat.SamplesPerBlock, "Channels");
+ Assert.AreEqual(7, adpcmWaveFormat.NumCoefficients, "NumCoefficients");
+ Assert.AreEqual(256, adpcmWaveFormat.Coefficients[0], "Coefficient 0");
+ Assert.AreEqual(0, adpcmWaveFormat.Coefficients[1], "Coefficient 1");
+ Assert.AreEqual(512, adpcmWaveFormat.Coefficients[2], "Coefficient 2");
+ Assert.AreEqual(-256, adpcmWaveFormat.Coefficients[3], "Coefficient 3");
+ Assert.AreEqual(0, adpcmWaveFormat.Coefficients[4], "Coefficient 4");
+ Assert.AreEqual(0, adpcmWaveFormat.Coefficients[5], "Coefficient 5");
+ Assert.AreEqual(192, adpcmWaveFormat.Coefficients[6], "Coefficient 6");
+ Assert.AreEqual(64, adpcmWaveFormat.Coefficients[7], "Coefficient 7");
+ Assert.AreEqual(240, adpcmWaveFormat.Coefficients[8], "Coefficient 8");
+ Assert.AreEqual(0, adpcmWaveFormat.Coefficients[9], "Coefficient 9");
+ Assert.AreEqual(460, adpcmWaveFormat.Coefficients[10], "Coefficient 10");
+ Assert.AreEqual(-208, adpcmWaveFormat.Coefficients[11], "Coefficient 11");
+ Assert.AreEqual(392, adpcmWaveFormat.Coefficients[12], "Coefficient 12");
+ Assert.AreEqual(-232, adpcmWaveFormat.Coefficients[13], "Coefficient 13");
+ }
+ }
+}
diff --git a/NAudioTests/WaveIn/WaveInDevicesTests.cs b/NAudioTests/WaveIn/WaveInDevicesTests.cs
new file mode 100644
index 00000000..d5a8f65e
--- /dev/null
+++ b/NAudioTests/WaveIn/WaveInDevicesTests.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+
+namespace NAudioTests
+{
+ [TestFixture]
+ [Category("IntegrationTest")]
+ public class WaveInDevicesTests
+ {
+ [Test]
+ public void CanRequestNumberOfWaveInDevices()
+ {
+ int deviceCount = WaveIn.DeviceCount;
+ Assert.That(deviceCount > 0, "Expected at least one WaveIn device");
+ }
+
+ [Test]
+ public void CanGetWaveInDeviceCapabilities()
+ {
+ for (int n = 0; n < WaveIn.DeviceCount; n++)
+ {
+ WaveInCapabilities capabilities = WaveIn.GetCapabilities(n);
+ Assert.IsNotNull(capabilities, "Null capabilities");
+ //Assert.That(capabilities.Channels >= 1, "At least one channel"); - seem to get -1 a lot
+ Assert.That(!String.IsNullOrEmpty(capabilities.ProductName), "Needs a name");
+ }
+ }
+
+ [Test]
+ public void CanGetWaveInCaps2NamesFromRegistry()
+ {
+ for (int n = 0; n < WaveIn.DeviceCount; n++)
+ {
+ WaveInCapabilities capabilities = WaveIn.GetCapabilities(n);
+ Console.WriteLine("PName: {0}", capabilities.ProductName);
+ Console.WriteLine("Name: {0} {1}", capabilities.NameGuid, WaveCapabilitiesHelpers.GetNameFromGuid(capabilities.NameGuid));
+ Console.WriteLine("Product: {0} {1}", capabilities.ProductGuid, WaveCapabilitiesHelpers.GetNameFromGuid(capabilities.ProductGuid));
+ Console.WriteLine("Manufacturer: {0} {1}", capabilities.ManufacturerGuid, WaveCapabilitiesHelpers.GetNameFromGuid(capabilities.ManufacturerGuid));
+ }
+ }
+
+
+ [Test]
+ public void CanGetWaveOutCaps2NamesFromRegistry()
+ {
+ for (int n = 0; n < WaveOut.DeviceCount; n++)
+ {
+ var capabilities = WaveOut.GetCapabilities(n);
+ Console.WriteLine("PName: {0}", capabilities.ProductName);
+ Console.WriteLine("Name: {0} {1}", capabilities.NameGuid, WaveCapabilitiesHelpers.GetNameFromGuid(capabilities.NameGuid));
+ Console.WriteLine("Product: {0} {1}", capabilities.ProductGuid, WaveCapabilitiesHelpers.GetNameFromGuid(capabilities.ProductGuid));
+ Console.WriteLine("Manufacturer: {0} {1}", capabilities.ManufacturerGuid, WaveCapabilitiesHelpers.GetNameFromGuid(capabilities.ManufacturerGuid));
+ }
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/BlockAlignmentReductionStreamTests.cs b/NAudioTests/WaveStreams/BlockAlignmentReductionStreamTests.cs
new file mode 100644
index 00000000..c018895e
--- /dev/null
+++ b/NAudioTests/WaveStreams/BlockAlignmentReductionStreamTests.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudioTests.Utils;
+using NAudio.Wave;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class BlockAlignmentReductionStreamTests
+ {
+ [Test]
+ public void CanCreateBlockAlignmentReductionStream()
+ {
+ BlockAlignedWaveStream inputStream = new BlockAlignedWaveStream(726, 80000);
+ BlockAlignReductionStream blockStream = new BlockAlignReductionStream(inputStream);
+ Assert.AreEqual(726, inputStream.BlockAlign);
+ Assert.AreEqual(2, blockStream.BlockAlign);
+ }
+
+ [Test]
+ public void CanReadNonBlockAlignedLengths()
+ {
+ BlockAlignedWaveStream inputStream = new BlockAlignedWaveStream(726, 80000);
+ BlockAlignReductionStream blockStream = new BlockAlignReductionStream(inputStream);
+
+
+ byte[] inputBuffer = new byte[1024];
+ int read = blockStream.Read(inputBuffer, 0, 1024);
+ Assert.AreEqual(1024, read, "bytes read 1");
+ Assert.AreEqual(blockStream.Position, 1024);
+ CheckReadBuffer(inputBuffer, 1024, 0);
+
+ read = blockStream.Read(inputBuffer, 0, 1024);
+ Assert.AreEqual(1024, read, "bytes read 2");
+ Assert.AreEqual(2048, blockStream.Position, "position 2");
+ CheckReadBuffer(inputBuffer, 1024, 1024);
+
+
+
+ }
+
+ [Test]
+ public void CanRepositionToNonBlockAlignedPositions()
+ {
+ BlockAlignedWaveStream inputStream = new BlockAlignedWaveStream(726, 80000);
+ BlockAlignReductionStream blockStream = new BlockAlignReductionStream(inputStream);
+
+
+ byte[] inputBuffer = new byte[1024];
+ int read = blockStream.Read(inputBuffer, 0, 1024);
+ Assert.AreEqual(1024, read, "bytes read 1");
+ Assert.AreEqual(blockStream.Position, 1024);
+ CheckReadBuffer(inputBuffer, 1024, 0);
+
+ read = blockStream.Read(inputBuffer, 0, 1024);
+ Assert.AreEqual(1024, read, "bytes read 2");
+ Assert.AreEqual(2048, blockStream.Position, "position 2");
+ CheckReadBuffer(inputBuffer, 1024, 1024);
+
+
+ // can reposition correctly
+ blockStream.Position = 1000;
+ read = blockStream.Read(inputBuffer, 0, 1024);
+ Assert.AreEqual(1024, read, "bytes read 3");
+ Assert.AreEqual(2024, blockStream.Position, "position 3");
+ CheckReadBuffer(inputBuffer, 1024, 1000);
+
+ }
+
+ private void CheckReadBuffer(byte[] readBuffer, int count, int startPosition)
+ {
+ for (int n = 0; n < count; n++)
+ {
+ byte expected = (byte)((startPosition + n) % 256);
+ Assert.AreEqual(expected, readBuffer[n],"Read buffer at position {0}",startPosition+ n);
+ }
+ }
+
+ }
+}
diff --git a/NAudioTests/WaveStreams/BufferedWaveProviderTests.cs b/NAudioTests/WaveStreams/BufferedWaveProviderTests.cs
new file mode 100644
index 00000000..a660943c
--- /dev/null
+++ b/NAudioTests/WaveStreams/BufferedWaveProviderTests.cs
@@ -0,0 +1,35 @@
+using System;
+using System.ComponentModel;
+using System.Linq;
+using NAudio.Wave;
+using NUnit.Framework;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class BufferedWaveProviderTests
+ {
+ [Test]
+ public void CanClearBeforeWritingSamples()
+ {
+ var bwp = new BufferedWaveProvider(new WaveFormat(44100, 16, 2));
+ bwp.ClearBuffer();
+ Assert.AreEqual(0, bwp.BufferedBytes);
+ }
+
+ [Test]
+ public void BufferedBytesAreReturned()
+ {
+ var bytesToBuffer = 1000;
+ var bwp = new BufferedWaveProvider(new WaveFormat(44100, 16, 2));
+ var data = Enumerable.Range(1, bytesToBuffer).Select(n => (byte)(n % 256)).ToArray();
+ bwp.AddSamples(data, 0, data.Length);
+ Assert.AreEqual(bytesToBuffer, bwp.BufferedBytes);
+ var readBuffer = new byte[bytesToBuffer];
+ var bytesRead = bwp.Read(readBuffer, 0, bytesToBuffer);
+ Assert.AreEqual(bytesRead, bytesToBuffer);
+ Assert.AreEqual(readBuffer,data);
+ Assert.AreEqual(0, bwp.BufferedBytes);
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/ChunkIdentifierTests.cs b/NAudioTests/WaveStreams/ChunkIdentifierTests.cs
new file mode 100644
index 00000000..a74f234d
--- /dev/null
+++ b/NAudioTests/WaveStreams/ChunkIdentifierTests.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Utils;
+using NAudio.Wave;
+using NUnit.Framework;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class ChunkIdentifierTests
+ {
+ [TestCase("WAVE")]
+ [TestCase("data")]
+ [TestCase("fmt ")]
+ [TestCase("RF64")]
+ [TestCase("ds64")]
+ [TestCase("labl")]
+ [TestCase("cue ")]
+ public void CanConvertChunkIndentiferToInt(string chunkIdentifier)
+ {
+ var x = WaveInterop.mmioStringToFOURCC(chunkIdentifier, 0);
+ Assert.AreEqual(x, ChunkIdentifier.ChunkIdentifierToInt32(chunkIdentifier));
+ }
+
+
+
+ }
+}
diff --git a/NAudioTests/WaveStreams/CircularBufferTests.cs b/NAudioTests/WaveStreams/CircularBufferTests.cs
new file mode 100644
index 00000000..3831c5f7
--- /dev/null
+++ b/NAudioTests/WaveStreams/CircularBufferTests.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Utils;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class CircularBufferTests
+ {
+ [Test]
+ public void CircularBufferHasMaxLengthAndCount()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(1024);
+ Assert.AreEqual(1024, circularBuffer.MaxLength);
+ Assert.AreEqual(0, circularBuffer.Count);
+ }
+
+ [Test]
+ public void ReadFromEmptyBufferReturnsNothing()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(1024);
+ byte[] buffer = new byte[1024];
+ int read = circularBuffer.Read(buffer, 0, 1024);
+ Assert.AreEqual(0, read);
+ }
+
+ [Test]
+ public void CanWriteToBuffer()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(1024);
+ byte[] buffer = new byte[100];
+ circularBuffer.Write(buffer, 0, 100);
+ Assert.AreEqual(100, circularBuffer.Count);
+ circularBuffer.Write(buffer, 0, 50);
+ Assert.AreEqual(150, circularBuffer.Count);
+ }
+
+ [Test]
+ public void BufferReturnsAsMuchAsIsAvailable()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(1024);
+ byte[] buffer = new byte[100];
+ circularBuffer.Write(buffer, 0, 100);
+ Assert.AreEqual(100, circularBuffer.Count);
+ byte[] readBuffer = new byte[1000];
+ int read = circularBuffer.Read(readBuffer, 0, 1000);
+ Assert.AreEqual(100, read);
+ }
+
+ [Test]
+ public void RejectsTooMuchData()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(100);
+ byte[] buffer = new byte[200];
+
+ int written = circularBuffer.Write(buffer, 0, 200);
+ Assert.AreEqual(100, written, "Wrote the wrong amount");
+ }
+
+ [Test]
+ public void RejectsWhenFull()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(100);
+ byte[] buffer = new byte[200];
+ circularBuffer.Write(buffer, 0, 75);
+ int written = circularBuffer.Write(buffer, 0, 50);
+ Assert.AreEqual(25, written, "Wrote the wrong amount");
+ }
+
+ [Test]
+ public void RejectsWhenExactlyFull()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(100);
+ byte[] buffer = new byte[200];
+ circularBuffer.Write(buffer, 0, 100);
+ int written = circularBuffer.Write(buffer, 0, 50);
+ Assert.AreEqual(0, written, "Wrote the wrong amount");
+ }
+
+ [Test]
+ public void CanWritePastEnd()
+ {
+ CircularBuffer circularBuffer = new CircularBuffer(100);
+ byte[] buffer = new byte[200];
+ circularBuffer.Write(buffer, 0, 75);
+ Assert.AreEqual(75, circularBuffer.Count, "Initial count");
+ int read = circularBuffer.Read(buffer, 0, 75);
+ Assert.AreEqual(0, circularBuffer.Count, "Count after read");
+ Assert.AreEqual(75, read, "Bytes read");
+ // write wraps round
+ circularBuffer.Write(buffer, 0, 50);
+ Assert.AreEqual(50, circularBuffer.Count, "Count after wrap round");
+ // read wraps round
+ read = circularBuffer.Read(buffer, 0, 75);
+ Assert.AreEqual(50, read, "Bytes Read 2");
+ Assert.AreEqual(0, circularBuffer.Count, "Final Count");
+ }
+
+ [Test]
+ public void DataIntegrityTest()
+ {
+ byte[] numbers = new byte[256];
+ byte[] readBuffer = new byte[256];
+ for (int n = 0; n < 256; n++)
+ {
+ numbers[n] = (byte)n;
+ }
+
+ CircularBuffer circularBuffer = new CircularBuffer(300);
+ circularBuffer.Write(numbers, 0, 200);
+ Array.Clear(readBuffer, 0, readBuffer.Length);
+ int read = circularBuffer.Read(readBuffer, 0, 200);
+ Assert.AreEqual(200, read);
+ CheckBuffer(readBuffer, 0, read);
+
+ // now write past the end
+ circularBuffer.Write(numbers, 0, 200);
+ Array.Clear(readBuffer, 0, readBuffer.Length);
+ // now read past the end
+ read = circularBuffer.Read(readBuffer, 0, 200);
+ Assert.AreEqual(200, read);
+ CheckBuffer(readBuffer, 0, read);
+
+ }
+
+ public void CheckBuffer(byte[] buffer, int startNumber, int length)
+ {
+ for (int n = 0; n < length; n++)
+ {
+ Assert.AreEqual(startNumber + n, buffer[n], "Byte mismatch at offset {0}", n);
+ }
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/FadeInOutSampleProviderTests.cs b/NAudioTests/WaveStreams/FadeInOutSampleProviderTests.cs
new file mode 100644
index 00000000..9192ae66
--- /dev/null
+++ b/NAudioTests/WaveStreams/FadeInOutSampleProviderTests.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave.SampleProviders;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class FadeInOutSampleProviderTests
+ {
+ [Test]
+ public void CanFadeIn()
+ {
+ var source = new TestSampleProvider(10, 1); // 10 samples per second
+ source.UseConstValue = true;
+ source.ConstValue = 100;
+ var fade = new FadeInOutSampleProvider(source);
+ fade.BeginFadeIn(1000);
+ float[] buffer = new float[20];
+ int read = fade.Read(buffer, 0, 20);
+ Assert.AreEqual(20, read);
+ Assert.AreEqual(0, buffer[0]); // start of fade-in
+ Assert.AreEqual(50, buffer[5]); // half-way
+ Assert.AreEqual(100, buffer[10]); // fully fade in
+ Assert.AreEqual(100, buffer[15]); // fully fade in
+ }
+
+ [Test]
+ public void CanFadeOut()
+ {
+ var source = new TestSampleProvider(10, 1); // 10 samples per second
+ source.UseConstValue = true;
+ source.ConstValue = 100;
+ var fade = new FadeInOutSampleProvider(source);
+ fade.BeginFadeOut(1000);
+ float[] buffer = new float[20];
+ int read = fade.Read(buffer, 0, 20);
+ Assert.AreEqual(20, read);
+ Assert.AreEqual(100, buffer[0]); // start of fade-out
+ Assert.AreEqual(50, buffer[5]); // half-way
+ Assert.AreEqual(0, buffer[10]); // fully fade out
+ Assert.AreEqual(0, buffer[15]); // fully fade out
+ }
+
+ [Test]
+ public void FadeDurationCanBeLongerThanOneRead()
+ {
+ var source = new TestSampleProvider(10, 1); // 10 samples per second
+ source.UseConstValue = true;
+ source.ConstValue = 100;
+ var fade = new FadeInOutSampleProvider(source);
+ fade.BeginFadeIn(1000);
+ float[] buffer = new float[4];
+ int read = fade.Read(buffer, 0, 4);
+ Assert.AreEqual(4, read);
+ Assert.AreEqual(0, buffer[0]); // start of fade-in
+ Assert.AreEqual(10, buffer[1]);
+ Assert.AreEqual(20, buffer[2], 0.0001);
+ Assert.AreEqual(30, buffer[3], 0.0001);
+
+ read = fade.Read(buffer, 0, 4);
+ Assert.AreEqual(4, read);
+ Assert.AreEqual(40, buffer[0], 0.0001);
+ Assert.AreEqual(50, buffer[1], 0.0001);
+ Assert.AreEqual(60, buffer[2], 0.0001);
+ Assert.AreEqual(70, buffer[3], 0.0001);
+
+ read = fade.Read(buffer, 0, 4);
+ Assert.AreEqual(4, read);
+ Assert.AreEqual(80, buffer[0], 0.0001);
+ Assert.AreEqual(90, buffer[1], 0.0001);
+ Assert.AreEqual(100, buffer[2], 0.0001);
+ Assert.AreEqual(100, buffer[3]);
+ }
+
+ [Test]
+ public void WaveFormatReturnsSourceWaveFormat()
+ {
+ var source = new TestSampleProvider(10, 1); // 10 samples per second
+ var fade = new FadeInOutSampleProvider(source);
+ Assert.AreSame(source.WaveFormat, fade.WaveFormat);
+ }
+
+ [Test]
+ public void FadeWorksOverSamplePairs()
+ {
+ var source = new TestSampleProvider(10, 2); // 10 samples per second
+ source.UseConstValue = true;
+ source.ConstValue = 100;
+ var fade = new FadeInOutSampleProvider(source);
+ fade.BeginFadeIn(1000);
+ float[] buffer = new float[20];
+ int read = fade.Read(buffer, 0, 20);
+ Assert.AreEqual(20, read);
+ Assert.AreEqual(0, buffer[0]); // start of fade-in
+ Assert.AreEqual(0, buffer[1]); // start of fade-in
+ Assert.AreEqual(50, buffer[10]); // half-way
+ Assert.AreEqual(50, buffer[11]); // half-way
+ Assert.AreEqual(90, buffer[18], 0.0001); // fully fade in
+ Assert.AreEqual(90, buffer[19], 0.0001); // fully fade in
+ }
+
+ [Test]
+ public void BufferIsZeroedAfterFadeOut()
+ {
+ var source = new TestSampleProvider(10, 1); // 10 samples per second
+ source.UseConstValue = true;
+ source.ConstValue = 100;
+ var fade = new FadeInOutSampleProvider(source);
+ fade.BeginFadeOut(1000);
+ float[] buffer = new float[20];
+ int read = fade.Read(buffer, 0, 20);
+ Assert.AreEqual(20, read);
+ Assert.AreEqual(100, buffer[0]); // start of fade-in
+ Assert.AreEqual(50, buffer[5]); // half-way
+ Assert.AreEqual(0, buffer[10]); // half-way
+ read = fade.Read(buffer, 0, 20);
+ Assert.AreEqual(20, read);
+ Assert.AreEqual(0, buffer[0]);
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/MixingSampleProviderTests.cs b/NAudioTests/WaveStreams/MixingSampleProviderTests.cs
new file mode 100644
index 00000000..e73ec744
--- /dev/null
+++ b/NAudioTests/WaveStreams/MixingSampleProviderTests.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+using NAudio.Wave.SampleProviders;
+using NUnit.Framework;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class MixingSampleProviderTests
+ {
+ [Test]
+ public void WithNoInputsFirstReadReturnsNoSamples()
+ {
+ var msp = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(44100, 2));
+ Assert.AreEqual(0, msp.Read(new float[1000], 0, 1000));
+ }
+
+ [Test]
+ public void WithReadFullySetNoInputsReturnsSampleCountRequested()
+ {
+ var msp = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(44100, 2));
+ msp.ReadFully = true;
+ var buffer = new float[1000];
+ Assert.AreEqual(buffer.Length, msp.Read(buffer, 0, buffer.Length));
+ }
+
+ [Test]
+ public void WithOneInputReadsToTheEnd()
+ {
+ var input1 = new TestSampleProvider(44100, 2, 2000);
+ var msp = new MixingSampleProvider(new [] { input1});
+ var buffer = new float[1000];
+ Assert.AreEqual(buffer.Length, msp.Read(buffer, 0, buffer.Length));
+ // randomly check one value
+ Assert.AreEqual(567, buffer[567]);
+ }
+
+ [Test]
+ public void WithOneInputReturnsSamplesReadIfNotEnoughToFullyRead()
+ {
+ var input1 = new TestSampleProvider(44100, 2, 800);
+ var msp = new MixingSampleProvider(new[] { input1 });
+ var buffer = new float[1000];
+ Assert.AreEqual(800, msp.Read(buffer, 0, buffer.Length));
+ // randomly check one value
+ Assert.AreEqual(567, buffer[567]);
+ }
+
+ [Test]
+ public void FullyReadCausesPartialBufferToBeZeroedOut()
+ {
+ var input1 = new TestSampleProvider(44100, 2, 800);
+ var msp = new MixingSampleProvider(new[] { input1 });
+ msp.ReadFully = true;
+ // of 1000 floats of value 9999
+ var buffer = Enumerable.Range(1,1000).Select(n => 9999f).ToArray();
+
+ Assert.AreEqual(buffer.Length, msp.Read(buffer, 0, buffer.Length));
+ // check we get 800 samples, followed by zeroed out data
+ Assert.AreEqual(567f, buffer[567]);
+ Assert.AreEqual(799f, buffer[799]);
+ Assert.AreEqual(0, buffer[800]);
+ Assert.AreEqual(0, buffer[999]);
+ }
+
+ }
+}
diff --git a/NAudioTests/WaveStreams/MonoToStereoProvider16Tests.cs b/NAudioTests/WaveStreams/MonoToStereoProvider16Tests.cs
new file mode 100644
index 00000000..7cc813c8
--- /dev/null
+++ b/NAudioTests/WaveStreams/MonoToStereoProvider16Tests.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class MonoToStereoProvider16Tests
+ {
+ [Test]
+ public void LeftChannelOnly()
+ {
+ IWaveProvider monoStream = new TestMonoProvider();
+ MonoToStereoProvider16 stereo = new MonoToStereoProvider16(monoStream);
+ stereo.LeftVolume = 1.0f;
+ stereo.RightVolume = 0.0f;
+ int samples = 1000;
+ byte[] buffer = new byte[samples * 2];
+ int read = stereo.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(buffer.Length, read, "bytes read");
+ WaveBuffer waveBuffer = new WaveBuffer(buffer);
+ short expected = 0;
+ for (int sample = 0; sample < samples; sample+=2)
+ {
+ short sampleLeft = waveBuffer.ShortBuffer[sample];
+ short sampleRight = waveBuffer.ShortBuffer[sample+1];
+ Assert.AreEqual(expected++, sampleLeft, "sample left");
+ Assert.AreEqual(0, sampleRight, "sample right");
+ }
+ }
+ }
+
+ class TestMonoProvider : WaveProvider16
+ {
+ short current;
+
+ public override int Read(short[] buffer, int offset, int sampleCount)
+ {
+ for (int sample = 0; sample < sampleCount; sample++)
+ {
+ buffer[offset + sample] = current++;
+ }
+ return sampleCount;
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/MultiplexingSampleProviderTests.cs b/NAudioTests/WaveStreams/MultiplexingSampleProviderTests.cs
new file mode 100644
index 00000000..e735bbd4
--- /dev/null
+++ b/NAudioTests/WaveStreams/MultiplexingSampleProviderTests.cs
@@ -0,0 +1,241 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NAudioTests.Utils;
+using NUnit.Framework;
+using NAudio.Wave.SampleProviders;
+using NAudio.Wave;
+using System.Diagnostics;
+using Moq;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class MultiplexingSampleProviderTests
+ {
+ [Test]
+ public void NullInputsShouldThrowException()
+ {
+ Assert.Throws(() => new MultiplexingSampleProvider(null, 1));
+ }
+
+ [Test]
+ public void ZeroInputsShouldThrowException()
+ {
+ Assert.Throws(() => new MultiplexingSampleProvider(new ISampleProvider[] { }, 1));
+ }
+
+ [Test]
+ public void ZeroOutputsShouldThrowException()
+ {
+ var input1 = new Mock();
+ Assert.Throws(() => new MultiplexingSampleProvider(new ISampleProvider[] { input1.Object }, 0));
+ }
+
+ [Test]
+ public void InvalidWaveFormatShouldThowException()
+ {
+ var input1 = new Mock();
+ input1.Setup(x => x.WaveFormat).Returns(new WaveFormat(32000, 16, 1));
+ Assert.Throws(() => new MultiplexingSampleProvider(new ISampleProvider[] { input1.Object }, 1));
+ }
+
+ [Test]
+ public void OneInOneOutShouldCopyWaveFormat()
+ {
+ var input1 = new Mock();
+ var inputWaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(32000, 1);
+ input1.Setup(x => x.WaveFormat).Returns(inputWaveFormat);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1.Object }, 1);
+ Assert.AreEqual(inputWaveFormat, mp.WaveFormat);
+ }
+
+ [Test]
+ public void OneInTwoOutShouldCopyWaveFormatButBeStereo()
+ {
+ var input1 = new Mock();
+ var inputWaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(32000, 1);
+ input1.Setup(x => x.WaveFormat).Returns(inputWaveFormat);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1.Object }, 2);
+ var expectedOutputWaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(32000, 2);
+ Assert.AreEqual(expectedOutputWaveFormat, mp.WaveFormat);
+ }
+
+ [Test]
+ public void OneInOneOutShouldCopyInReadMethod()
+ {
+ var input1 = new TestSampleProvider(32000, 1);
+ float[] expected = new float[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ mp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void OneInTwoOutShouldConvertMonoToStereo()
+ {
+ var input1 = new TestSampleProvider(32000, 1);
+ float[] expected = new float[] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 2);
+ mp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void TwoInOneOutShouldSelectLeftChannel()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ float[] expected = new float[] { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ mp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void TwoInOneOutShouldCanBeConfiguredToSelectRightChannel()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ float[] expected = new float[] { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ mp.ConnectInputToOutput(1, 0);
+ mp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void StereoInTwoOutShouldCopyStereo()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ float[] expected = new float[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 2);
+ }
+
+ [Test]
+ public void TwoMonoInTwoOutShouldCreateStereo()
+ {
+ var input1 = new TestSampleProvider(32000, 1);
+ var input2 = new TestSampleProvider(32000, 1) { Position = 100 };
+ float[] expected = new float[] { 0, 100, 1, 101, 2, 102, 3, 103, 4, 104, 5, 105 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1, input2 }, 2);
+ mp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void StereoInTwoOutCanBeConfiguredToSwapLeftAndRight()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ float[] expected = new float[] { 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 2);
+ mp.ConnectInputToOutput(0, 1);
+ mp.ConnectInputToOutput(1, 0);
+ mp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void HasConnectInputToOutputMethod()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ mp.ConnectInputToOutput(1, 0);
+ }
+
+ [Test]
+ public void ConnectInputToOutputThrowsExceptionForInvalidInput()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ Assert.Throws(() => mp.ConnectInputToOutput(2, 0));
+ }
+
+ [Test]
+ public void ConnectInputToOutputThrowsExceptionForInvalidOutput()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ Assert.Throws(() => mp.ConnectInputToOutput(1, 1));
+ }
+
+ [Test]
+ public void InputChannelCountIsCorrect()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ var input2 = new TestSampleProvider(32000, 1);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1, input2 }, 1);
+ Assert.AreEqual(3, mp.InputChannelCount);
+ }
+
+ [Test]
+ public void OutputChannelCountIsCorrect()
+ {
+ var input1 = new TestSampleProvider(32000, 1);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 3);
+ Assert.AreEqual(3, mp.OutputChannelCount);
+ }
+
+ [Test]
+ public void ThrowsExceptionIfSampleRatesDiffer()
+ {
+ var input1 = new TestSampleProvider(32000, 2);
+ var input2 = new TestSampleProvider(44100, 1);
+ Assert.Throws(() => new MultiplexingSampleProvider(new ISampleProvider[] { input1, input2 }, 1));
+ }
+
+ [Test]
+ public void ReadReturnsZeroIfSingleInputHasReachedEnd()
+ {
+ var input1 = new TestSampleProvider(32000, 1, 0);
+ float[] expected = new float[] { };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ float[] buffer = new float[10];
+ var read = mp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(0, read);
+ }
+
+ [Test]
+ public void ReadReturnsCountIfOneInputHasEndedButTheOtherHasnt()
+ {
+ var input1 = new TestSampleProvider(32000, 1, 0);
+ var input2 = new TestSampleProvider(32000, 1);
+ float[] expected = new float[] { 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1, input2 }, 2);
+ mp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void ShouldZeroOutBufferIfInputStopsShort()
+ {
+ var input1 = new TestSampleProvider(32000, 1, 6);
+ float[] expected = new float[] { 0, 1, 2, 3, 4, 5, 0, 0, 0, 0 };
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1 }, 1);
+ float[] buffer = new float[10];
+ for (int n = 0; n < buffer.Length; n++)
+ {
+ buffer[n] = 99;
+ }
+ var read = mp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(6, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ public void PerformanceTest()
+ {
+ var input1 = new TestSampleProvider(32000, 1);
+ var input2 = new TestSampleProvider(32000, 1);
+ var input3 = new TestSampleProvider(32000, 1);
+ var input4 = new TestSampleProvider(32000, 1);
+ var mp = new MultiplexingSampleProvider(new ISampleProvider[] { input1, input2, input3, input4 }, 4);
+ mp.ConnectInputToOutput(0, 3);
+ mp.ConnectInputToOutput(1, 2);
+ mp.ConnectInputToOutput(2, 1);
+ mp.ConnectInputToOutput(3, 0);
+
+ float[] buffer = new float[input1.WaveFormat.AverageBytesPerSecond / 4];
+ Stopwatch s = new Stopwatch();
+ var duration = s.Time(() =>
+ {
+ // read one hour worth of audio
+ for (int n = 0; n < 60 * 60; n++)
+ {
+ mp.Read(buffer, 0, buffer.Length);
+ }
+ });
+ Console.WriteLine("Performance test took {0}ms", duration);
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/MultiplexingWaveProviderTests.cs b/NAudioTests/WaveStreams/MultiplexingWaveProviderTests.cs
new file mode 100644
index 00000000..8b6693f8
--- /dev/null
+++ b/NAudioTests/WaveStreams/MultiplexingWaveProviderTests.cs
@@ -0,0 +1,313 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+using System.Diagnostics;
+using Moq;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class MultiplexingWaveProviderTests
+ {
+ [Test]
+ public void NullInputsShouldThrowException()
+ {
+ Assert.Throws(() => new MultiplexingWaveProvider(null, 1));
+ }
+
+ [Test]
+ public void ZeroInputsShouldThrowException()
+ {
+ Assert.Throws(() => new MultiplexingWaveProvider(new IWaveProvider[] { }, 1));
+ }
+
+ [Test]
+ public void ZeroOutputsShouldThrowException()
+ {
+ var input1 = new Mock();
+ Assert.Throws(() => new MultiplexingWaveProvider(new IWaveProvider[] { input1.Object }, 0));
+ }
+
+ [Test]
+ public void InvalidWaveFormatShouldThowException()
+ {
+ var input1 = new Mock();
+ input1.Setup(x => x.WaveFormat).Returns(new Gsm610WaveFormat());
+ Assert.Throws(() => new MultiplexingWaveProvider(new IWaveProvider[] { input1.Object }, 1));
+ }
+
+ [Test]
+ public void OneInOneOutShouldCopyWaveFormat()
+ {
+ var input1 = new Mock();
+ var inputWaveFormat = new WaveFormat(32000, 16, 1);
+ input1.Setup(x => x.WaveFormat).Returns(inputWaveFormat);
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1.Object }, 1);
+ Assert.AreEqual(inputWaveFormat, mp.WaveFormat);
+ }
+
+ [Test]
+ public void OneInTwoOutShouldCopyWaveFormatButBeStereo()
+ {
+ var input1 = new Mock();
+ var inputWaveFormat = new WaveFormat(32000, 16, 1);
+ input1.Setup(x => x.WaveFormat).Returns(inputWaveFormat);
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1.Object }, 2);
+ var expectedOutputWaveFormat = new WaveFormat(32000, 16, 2);
+ Assert.AreEqual(expectedOutputWaveFormat, mp.WaveFormat);
+ }
+
+ [Test]
+ public void OneInOneOutShouldCopyInReadMethod()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 1));
+ byte[] expected = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ byte[] buffer = new byte[10];
+ var read = mp.Read(buffer, 0, 10);
+ Assert.AreEqual(10, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+
+ [Test]
+ public void OneInTwoOutShouldConvertMonoToStereo()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 1));
+ // 16 bit so left right pairs
+ byte[] expected = new byte[] { 0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5, 6, 7, 6, 7, 8, 9, 8, 9 };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 2);
+ byte[] buffer = new byte[20];
+ var read = mp.Read(buffer, 0, 20);
+ Assert.AreEqual(20, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void TwoInOneOutShouldSelectLeftChannel()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ // 16 bit so left right pairs
+ byte[] expected = new byte[] { 0, 1, 4, 5, 8, 9, 12, 13, 16, 17 };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ byte[] buffer = new byte[10];
+ var read = mp.Read(buffer, 0, 10);
+ Assert.AreEqual(10, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void TwoInOneOutShouldCanBeConfiguredToSelectRightChannel()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ // 16 bit so left right pairs
+ byte[] expected = new byte[] { 2, 3, 6, 7, 10, 11, 14, 15, 18, 19 };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ mp.ConnectInputToOutput(1, 0);
+ byte[] buffer = new byte[10];
+ var read = mp.Read(buffer, 0, 10);
+ Assert.AreEqual(10, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void StereoInTwoOutShouldCopyStereo()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ // 4 bytes per pair of samples
+ byte[] expected = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 2);
+ byte[] buffer = new byte[12];
+ var read = mp.Read(buffer, 0, 12);
+ Assert.AreEqual(12, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void TwoMonoInTwoOutShouldCreateStereo()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 1));
+ var input2 = new TestWaveProvider(new WaveFormat(32000, 16, 1)) { Position = 100 };
+ // 4 bytes per pair of samples
+ byte[] expected = new byte[] { 0, 1, 100, 101, 2, 3, 102, 103, 4, 5, 104, 105, };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1, input2 }, 2);
+ byte[] buffer = new byte[expected.Length];
+ var read = mp.Read(buffer, 0, expected.Length);
+ Assert.AreEqual(expected.Length, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void StereoInTwoOutCanBeConfiguredToSwapLeftAndRight()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ // 4 bytes per pair of samples
+ byte[] expected = new byte[] { 2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 2);
+ mp.ConnectInputToOutput(0, 1);
+ mp.ConnectInputToOutput(1, 0);
+ byte[] buffer = new byte[12];
+ var read = mp.Read(buffer, 0, 12);
+ Assert.AreEqual(12, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void HasConnectInputToOutputMethod()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ mp.ConnectInputToOutput(1, 0);
+ }
+
+ [Test]
+ public void ConnectInputToOutputThrowsExceptionForInvalidInput()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ Assert.Throws(() => mp.ConnectInputToOutput(2, 0));
+ }
+
+ [Test]
+ public void ConnectInputToOutputThrowsExceptionForInvalidOutput()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ Assert.Throws(() => mp.ConnectInputToOutput(1, 1));
+ }
+
+ [Test]
+ public void InputChannelCountIsCorrect()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ var input2 = new TestWaveProvider(new WaveFormat(32000, 16, 1));
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1, input2 }, 1);
+ Assert.AreEqual(3, mp.InputChannelCount);
+ }
+
+ [Test]
+ public void OutputChannelCountIsCorrect()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 1));
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 3);
+ Assert.AreEqual(3, mp.OutputChannelCount);
+ }
+
+ [Test]
+ public void ThrowsExceptionIfSampleRatesDiffer()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ var input2 = new TestWaveProvider(new WaveFormat(44100, 16, 1));
+ Assert.Throws(() => new MultiplexingWaveProvider(new IWaveProvider[] { input1, input2 }, 1));
+ }
+
+ [Test]
+ public void ThrowsExceptionIfBitDepthsDiffer()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 2));
+ var input2 = new TestWaveProvider(new WaveFormat(32000, 24, 1));
+ Assert.Throws(() => new MultiplexingWaveProvider(new IWaveProvider[] { input1, input2 }, 1));
+ }
+
+ [Test]
+ public void ReadReturnsZeroIfSingleInputHasReachedEnd()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 1), 0);
+ byte[] expected = new byte[] { };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ byte[] buffer = new byte[10];
+ var read = mp.Read(buffer, 0, 10);
+ Assert.AreEqual(0, read);
+ }
+
+ [Test]
+ public void ReadReturnsCountIfOneInputHasEndedButTheOtherHasnt()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 1), 0);
+ var input2 = new TestWaveProvider(new WaveFormat(32000, 16, 1));
+ byte[] expected = new byte[] { };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1, input2 }, 1);
+ byte[] buffer = new byte[10];
+ var read = mp.Read(buffer, 0, 10);
+ Assert.AreEqual(10, read);
+ }
+
+ [Test]
+ public void ShouldZeroOutBufferIfInputStopsShort()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 16, 1), 6);
+ byte[] expected = new byte[] { 0, 1, 2, 3, 4, 5, 0, 0, 0, 0 };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 1);
+ byte[] buffer = new byte[10];
+ for (int n = 0; n < buffer.Length; n++)
+ {
+ buffer[n] = 0xFF;
+ }
+ var read = mp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(6, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void CorrectlyHandles24BitAudio()
+ {
+ var input1 = new TestWaveProvider(new WaveFormat(32000, 24, 1));
+ byte[] expected = new byte[] { 0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8, 9, 10, 11, 9, 10, 11 };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 2);
+ byte[] buffer = new byte[expected.Length];
+ var read = mp.Read(buffer, 0, expected.Length);
+ Assert.AreEqual(expected.Length, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+ [Test]
+ public void CorrectlyHandlesIeeeFloat()
+ {
+ var input1 = new TestWaveProvider(WaveFormat.CreateIeeeFloatWaveFormat(32000, 1));
+ byte[] expected = new byte[] { 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 9, 10, 11, 8, 9, 10, 11, };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 2);
+ byte[] buffer = new byte[expected.Length];
+ var read = mp.Read(buffer, 0, expected.Length);
+ Assert.AreEqual(expected.Length, read);
+ Assert.AreEqual(expected, buffer);
+ }
+
+
+ [Test]
+ public void CorrectOutputFormatIsSetForIeeeFloat()
+ {
+ var input1 = new TestWaveProvider(WaveFormat.CreateIeeeFloatWaveFormat(32000, 1));
+ byte[] expected = new byte[] { 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 9, 10, 11, 8, 9, 10, 11, };
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1 }, 2);
+ Assert.AreEqual(WaveFormatEncoding.IeeeFloat, mp.WaveFormat.Encoding);
+ }
+
+ public void PerformanceTest()
+ {
+ var waveFormat = new WaveFormat(32000, 16, 1);
+ var input1 = new TestWaveProvider(waveFormat);
+ var input2 = new TestWaveProvider(waveFormat);
+ var input3 = new TestWaveProvider(waveFormat);
+ var input4 = new TestWaveProvider(waveFormat);
+ var mp = new MultiplexingWaveProvider(new IWaveProvider[] { input1, input2, input3, input4 }, 4);
+ mp.ConnectInputToOutput(0, 3);
+ mp.ConnectInputToOutput(1, 2);
+ mp.ConnectInputToOutput(2, 1);
+ mp.ConnectInputToOutput(3, 0);
+
+ byte[] buffer = new byte[waveFormat.AverageBytesPerSecond];
+ Stopwatch s = new Stopwatch();
+ var duration = s.Time(() =>
+ {
+ // read one hour worth of audio
+ for (int n = 0; n < 60 * 60; n++)
+ {
+ mp.Read(buffer, 0, buffer.Length);
+ }
+ });
+ Console.WriteLine("Performance test took {0}ms", duration);
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/OffsetSampleProviderTests.cs b/NAudioTests/WaveStreams/OffsetSampleProviderTests.cs
new file mode 100644
index 00000000..7adaf67e
--- /dev/null
+++ b/NAudioTests/WaveStreams/OffsetSampleProviderTests.cs
@@ -0,0 +1,251 @@
+using System;
+using System.Linq;
+using NAudio.Wave.SampleProviders;
+using NAudioTests.Utils;
+using NUnit.Framework;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class OffsetSampleProviderTests
+ {
+ [Test]
+ public void DefaultShouldPassStraightThrough()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source);
+
+ var expected = new float[] { 0, 1, 2, 3, 4, 5, 6 };
+ osp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void CanAddPreDelay()
+ {
+ var source = new TestSampleProvider(32000, 1) {Position = 10};
+ var osp = new OffsetSampleProvider(source) {DelayBySamples = 5};
+
+ var expected = new float[] { 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15 };
+ osp.AssertReadsExpected(expected);
+ }
+
+
+ [Test]
+ public void CanAddPreDelayUsingTimeSpan()
+ {
+ var source = new TestSampleProvider(100, 1) { Position = 10 };
+ var osp = new OffsetSampleProvider(source) { DelayBy = TimeSpan.FromSeconds(1) };
+
+ var expected = Enumerable.Range(0,100).Select(x => 0f)
+ .Concat(Enumerable.Range(10,10).Select(x => (float)x)).ToArray();
+ osp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void CanAddPreDelayToStereoSourceUsingTimeSpan()
+ {
+ var source = new TestSampleProvider(100, 2) { Position = 10 };
+ var osp = new OffsetSampleProvider(source) { DelayBy = TimeSpan.FromSeconds(1) };
+
+ var expected = Enumerable.Range(0, 200).Select(x => 0f)
+ .Concat(Enumerable.Range(10, 10).Select(x => (float)x)).ToArray();
+ osp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void SettingPreDelayUsingTimeSpanReturnsCorrectTimeSpan()
+ {
+ var source = new TestSampleProvider(100, 2) { Position = 10 };
+ var osp = new OffsetSampleProvider(source) { DelayBy = TimeSpan.FromSeconds(2.5) };
+
+ Assert.AreEqual(2500, (int) osp.DelayBy.TotalMilliseconds);
+ Assert.AreEqual(500, osp.DelayBySamples);
+ }
+
+ [Test]
+ public void CanSkipOver()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source) {SkipOverSamples = 17};
+
+ var expected = new float[] { 17,18,19,20,21,22,23,24 };
+ osp.AssertReadsExpected(expected);
+ }
+
+ [Test]
+ public void CanTake()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source) {TakeSamples = 7};
+
+ var expected = new float[] { 0, 1, 2, 3, 4, 5, 6 };
+ osp.AssertReadsExpected(expected, 10);
+ }
+
+
+ [Test]
+ public void CanTakeThirtySeconds()
+ {
+ var source = new TestSampleProvider(16000, 1);
+ var osp = new OffsetSampleProvider(source) { Take = TimeSpan.FromSeconds(30) };
+ var buffer = new float[16000];
+ var totalRead = 0;
+ while (true)
+ {
+ var read = osp.Read(buffer, 0, buffer.Length);
+ totalRead += read;
+ if (read == 0) break;
+ Assert.IsTrue(totalRead <= 480000);
+
+ }
+ Assert.AreEqual(480000, totalRead);
+
+ }
+
+ [Test]
+ public void CanAddLeadOut()
+ {
+ var source = new TestSampleProvider(32000, 1, 10);
+ var osp = new OffsetSampleProvider(source) {LeadOutSamples = 5};
+
+ var expected = new float[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0 };
+ osp.AssertReadsExpected(expected, 100);
+ var expected2 = new float[] { };
+ osp.AssertReadsExpected(expected2, 100);
+ }
+
+ [Test]
+ public void WaveFormatIsSampeAsSource()
+ {
+ var source = new TestSampleProvider(32000, 1, 10);
+ var osp = new OffsetSampleProvider(source);
+ Assert.AreEqual(source.WaveFormat, osp.WaveFormat);
+ }
+
+
+ [Test]
+ public void MaintainsPredelayState()
+ {
+ var source = new TestSampleProvider(32000, 1) {Position = 10};
+ var osp = new OffsetSampleProvider(source) {DelayBySamples = 10};
+
+ var expected = new float[] {0, 0, 0, 0, 0,};
+ osp.AssertReadsExpected(expected);
+ var expected2 = new float[] {0, 0, 0, 0, 0,};
+ osp.AssertReadsExpected(expected2);
+ var expected3 = new float[] {10, 11, 12, 13, 14, 15};
+ osp.AssertReadsExpected(expected3);
+ }
+
+ [Test]
+ public void MaintainsTakeState()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source) {TakeSamples = 15};
+
+ var expected = new float[] { 0, 1, 2, 3, 4, 5, 6, 7 };
+ osp.AssertReadsExpected(expected);
+ var expected2 = new float[] { 8, 9, 10, 11, 12, 13, 14 };
+ osp.AssertReadsExpected(expected2, 20);
+ }
+
+ [Test]
+ public void CantSetDelayBySamplesAfterCallingRead()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source);
+ var buffer = new float[10];
+ osp.Read(buffer, 0, buffer.Length);
+
+ Assert.Throws(() => osp.DelayBySamples = 4);
+ }
+
+ [Test]
+ public void CantSetLeadOutSamplesAfterCallingRead()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source);
+ var buffer = new float[10];
+ osp.Read(buffer, 0, buffer.Length);
+
+ Assert.Throws(() => osp.LeadOutSamples = 4);
+ }
+
+ [Test]
+ public void CantSetSkipOverSamplesAfterCallingRead()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source);
+ var buffer = new float[10];
+ osp.Read(buffer, 0, buffer.Length);
+
+ Assert.Throws(() => osp.SkipOverSamples = 4);
+ }
+
+ [Test]
+ public void CantSetTakeSamplesAfterCallingRead()
+ {
+ var source = new TestSampleProvider(32000, 1);
+ var osp = new OffsetSampleProvider(source);
+ var buffer = new float[10];
+ osp.Read(buffer, 0, buffer.Length);
+
+ Assert.Throws(() => osp.TakeSamples = 4);
+ }
+
+ [Test]
+ public void HandlesSkipOverEntireSourceCorrectly()
+ {
+ var source = new TestSampleProvider(32000, 1, 10);
+ var osp = new OffsetSampleProvider(source);
+ osp.SkipOverSamples = 20;
+
+ var expected = new float[] { };
+ osp.AssertReadsExpected(expected, 20);
+ }
+
+
+ [Test]
+ public void CantSetNonBlockAlignedDelayBySamples()
+ {
+ var source = new TestSampleProvider(32000, 2);
+ var osp = new OffsetSampleProvider(source);
+
+ var ex = Assert.Throws(() => osp.DelayBySamples = 3);
+ Assert.That(ex.Message.Contains("DelayBySamples"));
+ }
+
+ [Test]
+ public void CantSetNonBlockAlignedSkipOverSamples()
+ {
+ var source = new TestSampleProvider(32000, 2);
+ var osp = new OffsetSampleProvider(source);
+
+ var ex = Assert.Throws(() => osp.SkipOverSamples = 3);
+ Assert.That(ex.Message.Contains("SkipOverSamples"));
+ }
+
+ [Test]
+ public void CantSetNonBlockAlignedTakeSamples()
+ {
+ var source = new TestSampleProvider(32000, 2);
+ var osp = new OffsetSampleProvider(source);
+
+ var ex = Assert.Throws(() => osp.TakeSamples = 3);
+ Assert.That(ex.Message.Contains("TakeSamples"));
+ }
+
+
+ [Test]
+ public void CantSetNonBlockAlignedLeadOutSamples()
+ {
+ var source = new TestSampleProvider(32000, 2);
+ var osp = new OffsetSampleProvider(source);
+
+ var ex = Assert.Throws(() => osp.LeadOutSamples = 3);
+ Assert.That(ex.Message.Contains("LeadOutSamples"));
+ }
+
+ // TODO: Test that Read offset parameter is respected
+ }
+}
diff --git a/NAudioTests/WaveStreams/SampleToWaveProvider24Tests.cs b/NAudioTests/WaveStreams/SampleToWaveProvider24Tests.cs
new file mode 100644
index 00000000..96e28c2f
--- /dev/null
+++ b/NAudioTests/WaveStreams/SampleToWaveProvider24Tests.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+using NAudio.Wave.SampleProviders;
+using NUnit.Framework;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class SampleToWaveProvider24Tests
+ {
+ [Test]
+ public void ConvertAFile()
+ {
+ const string input = @"C:\Users\Mark\Downloads\Region-1.wav";
+ if (!File.Exists(input)) Assert.Ignore("Test file not found");
+ using (var reader = new WaveFileReader(input))
+ {
+ var sp = reader.ToSampleProvider();
+ var wp24 = new SampleToWaveProvider24(sp);
+ WaveFileWriter.CreateWaveFile(@"C:\Users\Mark\Downloads\Region1-24.wav", wp24);
+ }
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/StereoToMonoProvider16Tests.cs b/NAudioTests/WaveStreams/StereoToMonoProvider16Tests.cs
new file mode 100644
index 00000000..14f872a7
--- /dev/null
+++ b/NAudioTests/WaveStreams/StereoToMonoProvider16Tests.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ [Category("UnitTest")]
+ public class StereoToMonoProvider16Tests
+ {
+ [Test]
+ public void RightChannelOnly()
+ {
+ IWaveProvider stereoStream = new TestStereoProvider();
+ StereoToMonoProvider16 mono = new StereoToMonoProvider16(stereoStream);
+ mono.LeftVolume = 0.0f;
+ mono.RightVolume = 1.0f;
+ int samples = 1000;
+ byte[] buffer = new byte[samples * 2];
+ int read = mono.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(buffer.Length, read, "bytes read");
+ WaveBuffer waveBuffer = new WaveBuffer(buffer);
+ short expected = 0;
+ for (int sample = 0; sample < samples; sample++)
+ {
+ short sampleVal = waveBuffer.ShortBuffer[sample];
+ Assert.AreEqual(expected--, sampleVal, "sample #" + sample.ToString());
+ }
+ }
+ }
+
+ class TestStereoProvider : WaveProvider16
+ {
+ public TestStereoProvider()
+ : base(44100, 2)
+ { }
+
+ short current;
+
+ public override int Read(short[] buffer, int offset, int sampleCount)
+ {
+ for (int sample = 0; sample < sampleCount; sample+=2)
+ {
+ buffer[offset + sample] = current;
+ buffer[offset + sample + 1] = (short)(0 - current);
+ current++;
+ }
+ return sampleCount;
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/TestSampleProvider.cs b/NAudioTests/WaveStreams/TestSampleProvider.cs
new file mode 100644
index 00000000..e114218c
--- /dev/null
+++ b/NAudioTests/WaveStreams/TestSampleProvider.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudioTests.WaveStreams
+{
+ class TestSampleProvider : ISampleProvider
+ {
+ private int length;
+
+ public TestSampleProvider(int sampleRate, int channels, int length = Int32.MaxValue)
+ {
+ this.WaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(sampleRate, channels);
+ this.length = length;
+ }
+
+ public int Read(float[] buffer, int offset, int count)
+ {
+ int n = 0;
+ while (n < count && Position < length)
+ {
+ buffer[n + offset] = (UseConstValue) ? ConstValue : Position;
+ n++; Position++;
+ }
+ return n;
+ }
+
+ public WaveFormat WaveFormat { get; set; }
+
+ public int Position { get; set; }
+
+ public bool UseConstValue { get; set; }
+ public int ConstValue { get; set; }
+ }
+}
diff --git a/NAudioTests/WaveStreams/TestWaveProvider.cs b/NAudioTests/WaveStreams/TestWaveProvider.cs
new file mode 100644
index 00000000..83065170
--- /dev/null
+++ b/NAudioTests/WaveStreams/TestWaveProvider.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NAudio.Wave;
+
+namespace NAudioTests.WaveStreams
+{
+ class TestWaveProvider : IWaveProvider
+ {
+ private int length;
+ public int ConstValue { get; set; }
+
+ public TestWaveProvider(WaveFormat format, int lengthInBytes = Int32.MaxValue)
+ {
+ this.WaveFormat = format;
+ this.length = lengthInBytes;
+ this.ConstValue = -1;
+ }
+
+ public int Read(byte[] buffer, int offset, int count)
+ {
+ int n = 0;
+ while (n < count && Position < length)
+ {
+ buffer[n + offset] = (ConstValue == -1) ? (byte)Position : (byte)ConstValue;
+ n++; Position++;
+ }
+ return n;
+ }
+
+ public WaveFormat WaveFormat { get; set; }
+
+ public int Position { get; set; }
+ }
+}
diff --git a/NAudioTests/WaveStreams/VolumeWaveProvider16Tests.cs b/NAudioTests/WaveStreams/VolumeWaveProvider16Tests.cs
new file mode 100644
index 00000000..178945c8
--- /dev/null
+++ b/NAudioTests/WaveStreams/VolumeWaveProvider16Tests.cs
@@ -0,0 +1,95 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+using Moq;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class VolumeWaveProvider16Tests
+ {
+ [Test]
+ public void DefaultVolumeIs1()
+ {
+ var testProvider = new TestWaveProvider(new WaveFormat(44100, 16, 2));
+ VolumeWaveProvider16 vwp = new VolumeWaveProvider16(testProvider);
+ Assert.AreEqual(1.0f, vwp.Volume);
+ }
+
+ [Test]
+ public void PassesThroughSourceWaveFormat()
+ {
+ var testProvider = new TestWaveProvider(new WaveFormat(44100, 16, 2));
+ VolumeWaveProvider16 vwp = new VolumeWaveProvider16(testProvider);
+ Assert.AreSame(testProvider.WaveFormat, vwp.WaveFormat);
+ }
+
+ [Test]
+ public void PassesThroughDataUnchangedAtVolume1()
+ {
+ var testProvider= new TestWaveProvider(new WaveFormat(44100,16,2));
+ VolumeWaveProvider16 vwp = new VolumeWaveProvider16(testProvider);
+ byte[] buffer = new byte[20];
+ int bytesRead = vwp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(buffer.Length, bytesRead);
+ Assert.AreEqual(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 }, buffer);
+ }
+
+ [Test]
+ public void HalfVolumeWorks()
+ {
+ var testProvider = new TestWaveProvider(new WaveFormat(44100, 16, 2));
+ testProvider.ConstValue = 100;
+ VolumeWaveProvider16 vwp = new VolumeWaveProvider16(testProvider);
+ vwp.Volume = 0.5f;
+ byte[] buffer = new byte[4];
+ int bytesRead = vwp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(new byte[] { 50, 50, 50, 50 }, buffer);
+ }
+
+ [Test]
+ public void ZeroVolumeWorks()
+ {
+ var testProvider = new TestWaveProvider(new WaveFormat(44100, 16, 2));
+ testProvider.ConstValue = 100;
+ VolumeWaveProvider16 vwp = new VolumeWaveProvider16(testProvider);
+ vwp.Volume = 0f;
+ byte[] buffer = new byte[4];
+ int bytesRead = vwp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(new byte[] { 0, 0, 0, 0 }, buffer);
+ }
+
+ [Test]
+ public void DoubleVolumeWorks()
+ {
+ var testProvider = new TestWaveProvider(new WaveFormat(44100, 16, 1));
+ testProvider.ConstValue = 2;
+ short sampleValue = BitConverter.ToInt16(new byte[] { 2, 2 }, 0);
+ sampleValue = (short)(sampleValue * 2);
+
+ VolumeWaveProvider16 vwp = new VolumeWaveProvider16(testProvider);
+ vwp.Volume = 2f;
+ byte[] buffer = new byte[2];
+ int bytesRead = vwp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(BitConverter.GetBytes(sampleValue), buffer);
+ }
+
+ [Test]
+ public void DoubleVolumeClips()
+ {
+ var testProvider = new TestWaveProvider(new WaveFormat(44100, 16, 1));
+ testProvider.ConstValue = 100;
+ short sampleValue = BitConverter.ToInt16(new byte[] { 100, 100 }, 0);
+ sampleValue = Int16.MaxValue;
+
+ VolumeWaveProvider16 vwp = new VolumeWaveProvider16(testProvider);
+ vwp.Volume = 2f;
+ byte[] buffer = new byte[2];
+ int bytesRead = vwp.Read(buffer, 0, buffer.Length);
+ Assert.AreEqual(BitConverter.GetBytes(sampleValue), buffer);
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/WaveChannel32Tests.cs b/NAudioTests/WaveStreams/WaveChannel32Tests.cs
new file mode 100644
index 00000000..6e6acc74
--- /dev/null
+++ b/NAudioTests/WaveStreams/WaveChannel32Tests.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using NUnit.Framework;
+using NAudio.Wave;
+using NAudioTests.Utils;
+using System.IO;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class WaveChannel32Tests
+ {
+ [Test]
+ [Category("IntegrationTest")]
+ public void CanCreateWavFileFromWaveChannel32()
+ {
+ string inFile = @"F:\Recording\wav\pcm\16bit mono 8kHz.wav";
+ string outFile = @"F:\Recording\wav\pcm\32bit stereo 8kHz.wav";
+ if (!File.Exists(inFile))
+ {
+ Assert.Ignore("Input test file not found");
+ }
+ var audio32 = new WaveChannel32(new WaveFileReader(inFile));
+ audio32.PadWithZeroes = false;
+ WaveFileWriter.CreateWaveFile(outFile, audio32);
+ }
+ }
+}
diff --git a/NAudioTests/WaveStreams/WaveFileReaderTests.cs b/NAudioTests/WaveStreams/WaveFileReaderTests.cs
new file mode 100644
index 00000000..f5d10208
--- /dev/null
+++ b/NAudioTests/WaveStreams/WaveFileReaderTests.cs
@@ -0,0 +1,157 @@
+using System.Collections.Generic;
+using System.IO;
+using NAudio.Utils;
+using NAudio.Wave;
+using NUnit.Framework;
+using System.Diagnostics;
+using System;
+using NAudio.FileFormats.Wav;
+
+namespace NAudioTests.WaveStreams
+{
+ [TestFixture]
+ public class WaveFileReaderTests
+ {
+ [Test]
+ [Category("UnitTest")]
+ public void TestEmptyFile()
+ {
+ // arrange
+ var fileContents = new byte[]
+ {
+ 0x52, 0x49, 0x46, 0x46, // "RIFF"
+ 0x26, 0x00, 0x00, 0x00, // ChunkSize = 38
+ 0x57, 0x41, 0x56, 0x45, // "WAVE"
+ 0x66, 0x6d, 0x74, 0x20, // "fmt "
+ 0x12, 0x00, 0x00, 0x00, // Subchunk1Size = 18
+ 0x07, 0x00, 0x02, 0x00, // AudioFormat = 7, NumChannels = 2
+ 0x40, 0x1f, 0x00, 0x00, // SampleRate = 8000
+ 0x80, 0x3e, 0x00, 0x00, // ByteRate = 16000
+ 0x02, 0x00, 0x08, 0x00, // BlockAlign = 2, BitsPerSample = 8
+ 0x00, 0x00, // ExtraParamSize = 0
+ 0x64, 0x61, 0x74, 0x61, // "data"
+ 0x00, 0x00, 0x00, 0x00, // Subchunk2Size = 0
+ };
+ using (var inputStream = new MemoryStream(fileContents))
+ {
+ // act
+ var chunks = new List