First Commit

This commit is contained in:
2025-02-06 22:24:29 +08:00
parent ed7df4c81e
commit 7539e6a53c
18116 changed files with 6181499 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
<Application x:Class="csharp_example.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:csharp_example"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,25 @@
//////////////////////////////////////////////////////////////////////////////
///
/// C# example that manipulates mp3 audio files with SoundTouch library.
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// License for this source code file: Microsoft Public License(Ms-PL)
//
////////////////////////////////////////////////////////////////////////////////
using System.Windows;
namespace csharp_example
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@@ -0,0 +1,31 @@
<Window x:Class="csharp_example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:csharp_example"
mc:Ignorable="d"
Title="C# SoundTouch example" Height="250" Width="400" AllowDrop="True" Drop="Window_Drop">
<Grid Margin="0,0,0,-3">
<TextBlock HorizontalAlignment="Left" Margin="10,21,0,0" Text="Input audio file:" VerticalAlignment="Top"/>
<TextBox x:Name="textBox_filename" Height="23" Margin="107,20,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Width="180" IsEnabled="False"/>
<Button x:Name="button_browse" Content="_Browse" HorizontalAlignment="Left" Margin="292,19,0,0" VerticalAlignment="Top" Width="75" Height="24" Click="button_browse_Click"/>
<TextBlock HorizontalAlignment="Left" Margin="54,55,0,0" Text="Tempo:" VerticalAlignment="Top" TextAlignment="Right"/>
<TextBox x:Name="textBox_tempo" HorizontalAlignment="Left" Height="23" Margin="107,55,0,0" Text="0" VerticalAlignment="Top" Width="75" TextAlignment="Center" LostFocus="textBox_tempo_LostFocus" KeyDown="textBox_tempo_KeyDown"/>
<TextBlock HorizontalAlignment="Left" Margin="185,55,0,0" Text="%" VerticalAlignment="Top" TextAlignment="Right"/>
<TextBlock HorizontalAlignment="Left" Margin="64,85,0,0" Text="Pitch:" VerticalAlignment="Top" TextAlignment="Right"/>
<TextBox x:Name="textBox_pitch" HorizontalAlignment="Left" Height="23" Margin="107,85,0,0" Text="0" VerticalAlignment="Top" Width="75" TextAlignment="Center" LostFocus="textBox_pitch_LostFocus" KeyDown="textBox_pitch_KeyDown"/>
<TextBlock HorizontalAlignment="Left" Margin="185,85,0,0" Text="semitones" VerticalAlignment="Top" TextAlignment="Right"/>
<TextBlock HorizontalAlignment="Left" Margin="66,116,0,0" Text="Rate:" VerticalAlignment="Top" TextAlignment="Right"/>
<TextBox x:Name="textBox_rate" HorizontalAlignment="Left" Height="23" Margin="107,116,0,0" Text="0" VerticalAlignment="Top" Width="75" TextAlignment="Center" LostFocus="textBox_rate_LostFocus" KeyDown="textBox_rate_KeyDown"/>
<TextBlock HorizontalAlignment="Left" Margin="185,116,0,0" Text="%" VerticalAlignment="Top" TextAlignment="Right"/>
<Button x:Name="button_play" Content="_Play" Margin="107,150,0,0" VerticalAlignment="Top" Height="24" HorizontalAlignment="Left" Width="75" Click="button_play_Click" IsEnabled="False"/>
<Button x:Name="button_stop" Content="_Stop" Margin="200,150,0,0" VerticalAlignment="Top" Height="24" HorizontalAlignment="Left" Width="75" Click="button_stop_Click" IsEnabled="False"/>
<StatusBar VerticalAlignment="Bottom">
<TextBlock x:Name="text_status" HorizontalAlignment="Left" Margin="2,0,0,2"/>
</StatusBar>
</Grid>
</Window>

View File

@@ -0,0 +1,258 @@
//////////////////////////////////////////////////////////////////////////////
///
/// C# example that manipulates mp3 audio files with SoundTouch library.
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// License for this source code file: Microsoft Public License(Ms-PL)
//
////////////////////////////////////////////////////////////////////////////////
using soundtouch;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace csharp_example
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
protected SoundProcessor processor = new SoundProcessor();
public MainWindow()
{
InitializeComponent();
StatusMessage.statusEvent += StatusEventHandler;
processor.PlaybackStopped += EventHandler_playbackStopped;
DisplaySoundTouchVersion();
}
/// <summary>
/// Display SoundTouch library version string in status bar. This also indicates whether the DLL was loaded successfully or not ...
/// </summary>
private void DisplaySoundTouchVersion()
{
string status;
try
{
status = String.Format("SoundTouch version: {0}", SoundTouch.Version);
}
catch (Exception exp)
{
status = exp.Message;
}
text_status.Text = status;
}
private void StatusEventHandler(object sender, string msg)
{
text_status.Text = msg;
}
// Open mp3 file for playback
private void OpenFile(string fileName)
{
Stop();
if (processor.OpenMp3File(fileName) == true)
{
textBox_filename.Text = fileName;
button_play.IsEnabled = true;
button_stop.IsEnabled = true;
// Parse adjustment settings
ParseTempoTextBox();
ParsePitchTextBox();
ParseRateTextBox();
}
else
{
textBox_filename.Text = "";
button_play.IsEnabled = false;
button_stop.IsEnabled = false;
MessageBox.Show("Coudln't open audio file " + fileName);
}
}
private void button_browse_Click(object sender, RoutedEventArgs e)
{
// Show file selection dialog
Microsoft.Win32.OpenFileDialog openDialog = new Microsoft.Win32.OpenFileDialog();
if (string.IsNullOrEmpty(textBox_filename.Text) == false)
{
// if an audio file is open, set directory to same as with the file
openDialog.InitialDirectory = Path.GetDirectoryName(textBox_filename.Text);
}
openDialog.Filter = "MP3 files (*.mp3)|*.mp3";
if (openDialog.ShowDialog() == true)
{
OpenFile(openDialog.FileName);
}
}
private void setPlayButtonMode(bool play)
{
button_play.Content = play ? "_Play" : "_Pause";
}
private void EventHandler_playbackStopped(object sender, bool hasReachedEnd)
{
if (hasReachedEnd)
{
text_status.Text = "Stopped";
} // otherwise paused
setPlayButtonMode(true);
}
private void button_play_Click(object sender, RoutedEventArgs e)
{
if ((string)button_play.Content == "_Pause")
{
// Pause
if (processor.Pause())
{
text_status.Text = "Paused";
}
setPlayButtonMode(true);
}
else
{
// Play
if (processor.Play())
{
text_status.Text = "Playing";
setPlayButtonMode(false);
}
}
}
private void Stop()
{
if (processor.Stop())
{
text_status.Text = "Stopped";
}
setPlayButtonMode(true);
}
private void button_stop_Click(object sender, RoutedEventArgs e)
{
Stop();
}
private bool parse_percentValue(TextBox box, out double value)
{
if (double.TryParse(box.Text, out value) == false) return false;
if (value < -99.0) value = -99.0; // don't allow more than -100% slowdown ... :)
box.Text = value.ToString();
return true;
}
private void ParsePitchTextBox()
{
double pitchValue;
if (double.TryParse(textBox_pitch.Text, out pitchValue))
{
if (processor.streamProcessor != null) processor.streamProcessor.st.PitchSemiTones = (float)pitchValue;
}
}
private void ParseTempoTextBox()
{
double tempoValue;
if (parse_percentValue(textBox_tempo, out tempoValue))
{
if (processor.streamProcessor != null) processor.streamProcessor.st.TempoChange = (float)tempoValue;
}
}
private void ParseRateTextBox()
{
double rateValue;
if (parse_percentValue(textBox_rate, out rateValue))
{
if (processor.streamProcessor != null) processor.streamProcessor.st.RateChange = (float)rateValue;
}
}
private void textBox_tempo_LostFocus(object sender, RoutedEventArgs e)
{
ParseTempoTextBox();
}
private void textBox_tempo_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
// enter pressed -- parse value
ParseTempoTextBox();
}
}
private void textBox_pitch_LostFocus(object sender, RoutedEventArgs e)
{
ParsePitchTextBox();
}
private void textBox_pitch_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
// enter pressed -- parse value
ParsePitchTextBox();
}
}
private void textBox_rate_LostFocus(object sender, RoutedEventArgs e)
{
ParseRateTextBox();
}
private void textBox_rate_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
// enter pressed -- parse value
ParseRateTextBox();
}
}
// Handler for file drag & drop over the window
private void Window_Drop(object sender, DragEventArgs e)
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
// open 1st of the chosen files
OpenFile(files[0]);
}
}
}

View File

@@ -0,0 +1,31 @@
Microsoft Public License (Ms-PL)
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.

View File

@@ -0,0 +1,92 @@
NAudio is an open source .NET audio library written by Mark Heath (mark.heath@gmail.com)
For more information, visit http://naudio.codeplex.com
NAudio is now being hosted on GitHub http://github.com/naudio/NAudio
THANKS
======
The following list includes some of the people who have contributed in various ways to NAudio, such as code contributions,
bug fixes, documentation, helping out on the forums and even donations. I haven't finished compiling this list yet, so
if your name should be on it but isn't please let me know and I will include it. Also, some people I only know by their forum
id, so if you want me to put your full name here, please also get in touch.
in alphabetical order:
Alan Jordan
Alexandre Mutel
Alexander Binkert
AmandaTarafaMas
balistof
biermeester
borman11
bradb
Brandon Hansen (kg6ypi)
csechet
ChunkWare Music Software
CKing
DaMacc
Dirk Eckhardt
Du10
eejake52
Florian Rosmann (filoe)
Freefall
Giawa
Harald Petrovitsch
Hfuy
Iain McCowan
Idael Cardaso
ioctlLR
Ivan Kochurkin (KvanTTT)
Jamie Michael Ewins
jannera
jbaker8935
jcameron23
JoeGaggler
jonahoffmann
jontdelorme
Jospin Software
Justin Frankel
K24A3
Kamen Lichev
Kassoul
kevinxxx
kzych
LionCash
Lustild
Lucian Wischik (ljw1004)
ManuN
MeelMarcel
Michael Chadwick
Michael Feld
Michael J
Michael Lehenbauer
milligan22963
myrkle
nelsonkidd
Nigel Redmon
Nikolaos Georgiou
Owen Skriloff
owoudenb
painmailer
PPavan
Pygmy
Ray Molenkamp
Roadz
Robert Bristow-Johnson
Scott Fleischman
Simon Clark
Sirish Bajpai
sporn
Steve Underwood
Ted Murphy
Tiny Simple Tools
Tobias Fleming
TomBogle
Tony Cabello
Tony Sistemas
TuneBlade
topher3683
volmart
Vladimir Rokovanov
Ville Koskinen
Wyatt Rice
Yuval Naveh
Zsb

View File

@@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// 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("csharp-example")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("csharp-example")]
[assembly: AssemblyCopyright("Copyright © Olli Parviainen")]
[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)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// 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.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace csharp_example.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("csharp_example.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace csharp_example.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;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -0,0 +1,9 @@
csharp-example
==============
Copyright (c) Olli Parviainen
This is a C# example application that plays MP3 audio file and
uses SoundTouch.dll library for adjusting the sound in real-time.
The example uses NAudio library for MP3 file reading and audio playback.
See NAudio-readme.txt and NAudio-license.txt for NAudio information.

View File

@@ -0,0 +1,292 @@
//////////////////////////////////////////////////////////////////////////////
///
/// WaveStream processor class for manipulating audio stream in C# with
/// SoundTouch library.
///
/// This module uses NAudio library for C# audio file input / output
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// License for this source code file: Microsoft Public License(Ms-PL)
//
////////////////////////////////////////////////////////////////////////////////
using NAudio.Wave;
using soundtouch;
using System;
namespace csharp_example
{
/// <summary>
/// Helper class that allow writing status texts to the host application
/// </summary>
public class StatusMessage
{
/// <summary>
/// Handler for status message events. Subscribe this from the host application
/// </summary>
public static event EventHandler<string> statusEvent;
/// <summary>
/// Pass a status message to the host application
/// </summary>
public static void Write(string msg)
{
if (statusEvent != null)
{
statusEvent(null, msg);
}
}
}
/// <summary>
/// NAudui WaveStream class for processing audio stream with SoundTouch effects
/// </summary>
public class WaveStreamProcessor : WaveStream
{
private WaveChannel32 inputStr;
public SoundTouch st;
private byte[] bytebuffer = new byte[4096];
private float[] floatbuffer = new float[1024];
bool endReached = false;
/// <summary>
/// Constructor
/// </summary>
/// <param name="input">WaveChannel32 stream used for processor stream input</param>
public WaveStreamProcessor(WaveChannel32 input)
{
inputStr = input;
st = new SoundTouch();
st.Channels = (uint)input.WaveFormat.Channels;
st.SampleRate = (uint)input.WaveFormat.SampleRate;
}
/// <summary>
/// True if end of stream reached
/// </summary>
public bool EndReached
{
get { return endReached; }
}
public override long Length
{
get
{
return inputStr.Length;
}
}
public override long Position
{
get
{
return inputStr.Position;
}
set
{
inputStr.Position = value;
}
}
public override WaveFormat WaveFormat
{
get
{
return inputStr.WaveFormat;
}
}
/// <summary>
/// Overridden Read function that returns samples processed with SoundTouch. Returns data in same format as
/// WaveChannel32 i.e. stereo float samples.
/// </summary>
/// <param name="buffer">Buffer where to return sample data</param>
/// <param name="offset">Offset from beginning of the buffer</param>
/// <param name="count">Number of bytes to return</param>
/// <returns>Number of bytes copied to buffer</returns>
public override int Read(byte[] buffer, int offset, int count)
{
try
{
// Iterate until enough samples available for output:
// - read samples from input stream
// - put samples to SoundStretch processor
while (st.AvailableSampleCount < count)
{
int nbytes = inputStr.Read(bytebuffer, 0, bytebuffer.Length);
if (nbytes == 0)
{
// end of stream. flush final samples from SoundTouch buffers to output
if (endReached == false)
{
endReached = true; // do only once to avoid continuous flushing
st.Flush();
}
break;
}
// binary copy data from "byte[]" to "float[]" buffer
Buffer.BlockCopy(bytebuffer, 0, floatbuffer, 0, nbytes);
st.PutSamples(floatbuffer, (uint)(nbytes / 8));
}
// ensure that buffer is large enough to receive desired amount of data out
if (floatbuffer.Length < count / 4)
{
floatbuffer = new float[count / 4];
}
// get processed output samples from SoundTouch
int numsamples = (int)st.ReceiveSamples(floatbuffer, (uint)(count / 8));
// binary copy data from "float[]" to "byte[]" buffer
Buffer.BlockCopy(floatbuffer, 0, buffer, offset, numsamples * 8);
return numsamples * 8; // number of bytes
}
catch (Exception exp)
{
StatusMessage.Write("exception in WaveStreamProcessor.Read: " + exp.Message);
return 0;
}
}
/// <summary>
/// Clear the internal processor buffers. Call this if seeking or rewinding to new position within the stream.
/// </summary>
public void Clear()
{
st.Clear();
endReached = false;
}
}
/// <summary>
/// Class that opens & plays MP3 file and allows real-time audio processing with SoundTouch
/// while playing
/// </summary>
public class SoundProcessor
{
Mp3FileReader mp3File;
WaveOut waveOut;
public WaveStreamProcessor streamProcessor;
/// <summary>
/// Start / resume playback
/// </summary>
/// <returns>true if successful, false if audio file not open</returns>
public bool Play()
{
if (waveOut == null) return false;
if (waveOut.PlaybackState != PlaybackState.Playing)
{
waveOut.Play();
}
return true;
}
/// <summary>
/// Pause playback
/// </summary>
/// <returns>true if successful, false if audio not playing</returns>
public bool Pause()
{
if (waveOut == null) return false;
if (waveOut.PlaybackState == PlaybackState.Playing)
{
waveOut.Stop();
return true;
}
return false;
}
/// <summary>
/// Stop playback
/// </summary>
/// <returns>true if successful, false if audio file not open</returns>
public bool Stop()
{
if (waveOut == null) return false;
waveOut.Stop();
mp3File.Position = 0;
streamProcessor.Clear();
return true;
}
/// <summary>
/// Event for "playback stopped" event. 'bool' argument is true if playback has reached end of stream.
/// </summary>
public event EventHandler<bool> PlaybackStopped;
/// <summary>
/// Proxy event handler for receiving playback stopped event from WaveOut
/// </summary>
protected void EventHandler_stopped(object sender, StoppedEventArgs args)
{
bool isEnd = streamProcessor.EndReached;
if (isEnd)
{
Stop();
}
if (PlaybackStopped != null)
{
PlaybackStopped(sender, isEnd);
}
}
/// <summary>
/// Open MP3 file
/// </summary>
/// <param name="filePath">Path to file to open</param>
/// <returns>true if successful</returns>
public bool OpenMp3File(string filePath)
{
try
{
mp3File = new Mp3FileReader(filePath);
WaveChannel32 inputStream = new WaveChannel32(mp3File);
inputStream.PadWithZeroes = false; // don't pad, otherwise the stream never ends
streamProcessor = new WaveStreamProcessor(inputStream);
waveOut = new WaveOut()
{
DesiredLatency = 100
};
waveOut.Init(streamProcessor); // inputStream);
waveOut.PlaybackStopped += EventHandler_stopped;
StatusMessage.Write("Opened file " + filePath);
return true;
}
catch (Exception exp)
{
// Error in opening file
waveOut = null;
StatusMessage.Write("Can't open file: " + exp.Message);
return false;
}
}
}
}

View File

@@ -0,0 +1,681 @@
//////////////////////////////////////////////////////////////////////////////
///
/// C# wrapper to access SoundTouch APIs from an external SoundTouch.dll library
///
/// Author : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
/// The C# wrapper improved by Mario Di Vece
///
////////////////////////////////////////////////////////////////////////////////
//
// License :
//
// SoundTouch audio processing library
// Copyright (c) Olli Parviainen
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////
using System;
using System.Runtime.InteropServices;
namespace soundtouch
{
public sealed class SoundTouch : IDisposable
{
#region Internal Members
internal const string SoundTouchLibrary = "SoundTouch.dll";
#endregion
#region Private Members // hahaha what a curious region
private readonly object SyncRoot = new object();
private bool IsDisposed = false;
private IntPtr handle;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="SoundTouch"/> class.
/// </summary>
public SoundTouch()
{
handle = NativeMethods.CreateInstance();
}
/// <summary>
/// Finalizes an instance of the <see cref="SoundTouch"/> class.
/// </summary>
~SoundTouch()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
/// <summary>
/// Settings as defined in SoundTouch.h
/// </summary>
public enum Setting
{
/// <summary>
/// Enable/disable anti-alias filter in pitch transposer (0 = disable)
/// </summary>
UseAntiAliasFilter = 0,
/// <summary>
/// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32)
/// </summary>
AntiAliasFilterLength = 1,
/// <summary>
/// Enable/disable quick seeking algorithm in tempo changer routine
/// (enabling quick seeking lowers CPU utilization but causes a minor sound
/// quality compromising)
/// </summary>
UseQuickSeek = 2,
/// <summary>
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
/// See "STTypes.h" or README for more information.
/// </summary>
SequenceMilliseconds = 3,
/// <summary>
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
/// best possible overlapping location. This determines from how wide window the algorithm
/// may look for an optimal joining location when mixing the sound sequences back together.
/// See "STTypes.h" or README for more information.
/// </summary>
SeekWindowMilliseconds = 4,
/// <summary>
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
/// are mixed back together, to form a continuous sound stream, this parameter defines over
/// how long period the two consecutive sequences are let to overlap each other.
/// See "STTypes.h" or README for more information.
/// </summary>
OverlapMilliseconds = 5,
/// <summary>
/// Call "getSetting" with this ID to query processing sequence size in samples.
/// This value gives approximate value of how many input samples you'll need to
/// feed into SoundTouch after initial buffering to get out a new batch of
/// output samples.
///
/// This value does not include initial buffering at beginning of a new processing
/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
///
/// Notices:
/// - This is read-only parameter, i.e. setSetting ignores this parameter
/// - This parameter value is not constant but change depending on
/// tempo/pitch/rate/samplerate settings.
/// </summary>
NominalInputSequence = 6,
/// <summary>
/// Call "getSetting" with this ID to query nominal average processing output
/// size in samples. This value tells approcimate value how many output samples
/// SoundTouch outputs once it does DSP processing run for a batch of input samples.
///
/// Notices:
/// - This is read-only parameter, i.e. setSetting ignores this parameter
/// - This parameter value is not constant but change depending on
/// tempo/pitch/rate/samplerate settings.
/// </summary>
NominalOutputSequence = 7,
/// <summary>
/// Call "getSetting" with this ID to query initial processing latency, i.e.
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
/// you can expect to get first batch of ready output samples out.
///
/// After the first output batch, you can then expect to get approx.
/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every
/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch.
///
/// Example:
/// processing with parameter -tempo=5
/// => initial latency = 5509 samples
/// input sequence = 4167 samples
/// output sequence = 3969 samples
///
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
/// the stream, and then you'll get out the first 3969 samples. After that, for
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
/// 3969 samples out.
///
/// This also means that average latency during stream processing is
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
/// = 3524 samples
///
/// Notices:
/// - This is read-only parameter, i.e. setSetting ignores this parameter
/// - This parameter value is not constant but change depending on
/// tempo/pitch/rate/samplerate settings.
/// </summary>
InitialLatency = 8,
}
#endregion
#region Properties
/// <summary>
/// Get SoundTouch version string
/// </summary>
public static string Version
{
get
{
// convert "char *" data to c# string
return Marshal.PtrToStringAnsi(NativeMethods.GetVersionString());
}
}
/// <summary>
/// Gets a value indicating whether the SoundTouch Library (dll) is available
/// </summary>
public static bool IsAvailable
{
get
{
try
{
var versionId = NativeMethods.GetVersionId();
return versionId != 0;
}
catch
{
return false;
}
}
}
/// <summary>
/// Returns number of processed samples currently available in SoundTouch for immediate output.
/// </summary>
public uint AvailableSampleCount
{
get { lock (SyncRoot) { return NativeMethods.NumSamples(handle); } }
}
/// <summary>
/// Returns number of samples currently unprocessed in SoundTouch internal buffer
/// </summary>
/// <returns>Number of sample frames</returns>
public uint UnprocessedSampleCount
{
get { lock (SyncRoot) { return NativeMethods.NumUnprocessedSamples(handle); } }
}
/// <summary>
/// Check if there aren't any samples available for outputting.
/// </summary>
/// <returns>nonzero if there aren't any samples available for outputting</returns>
public int IsEmpty
{
get { lock (SyncRoot) { return NativeMethods.IsEmpty(handle); } }
}
/// <summary>
/// Sets the number of channels
///
/// Value: 1 = mono, 2 = stereo, n = multichannel
/// </summary>
public uint Channels
{
set { lock (SyncRoot) { NativeMethods.SetChannels(handle, value); } }
}
/// <summary>
/// Sets sample rate.
/// Value: Sample rate, e.g. 44100
/// </summary>
public uint SampleRate
{
set { lock (SyncRoot) { NativeMethods.SetSampleRate(handle, value); } }
}
/// <summary>
/// Sets new tempo control value.
///
/// Value: Tempo setting. Normal tempo = 1.0, smaller values
/// represent slower tempo, larger faster tempo.
/// </summary>
public float Tempo
{
set { lock (SyncRoot) { NativeMethods.SetTempo(handle, value); } }
}
/// <summary>
/// Sets new tempo control value as a difference in percents compared
/// to the original tempo (-50 .. +100 %);
/// </summary>
public float TempoChange
{
set { lock (SyncRoot) { NativeMethods.SetTempoChange(handle, value); } }
}
/// <summary>
/// Sets new rate control value.
/// Rate setting. Normal rate = 1.0, smaller values
/// represent slower rate, larger faster rate.
/// </summary>
public float Rate
{
set { lock (SyncRoot) { NativeMethods.SetTempo(handle, value); } }
}
/// <summary>
/// Sets new rate control value as a difference in percents compared
/// to the original rate (-50 .. +100 %);
///
/// Value: Rate setting is in %
/// </summary>
public float RateChange
{
set { lock (SyncRoot) { NativeMethods.SetRateChange(handle, value); } }
}
/// <summary>
/// Sets new pitch control value.
///
/// Value: Pitch setting. Original pitch = 1.0, smaller values
/// represent lower pitches, larger values higher pitch.
/// </summary>
public float Pitch
{
set { lock (SyncRoot) { NativeMethods.SetPitch(handle, value); } }
}
/// <summary>
/// Sets pitch change in octaves compared to the original pitch
/// (-1.00 .. +1.00 for +- one octave);
///
/// Value: Pitch setting in octaves
/// </summary>
public float PitchOctaves
{
set { lock (SyncRoot) { NativeMethods.SetPitchOctaves(handle, value); } }
}
/// <summary>
/// Sets pitch change in semi-tones compared to the original pitch
/// (-12 .. +12 for +- one octave);
///
/// Value: Pitch setting in semitones
/// </summary>
public float PitchSemiTones
{
set { lock (SyncRoot) { NativeMethods.SetPitchSemiTones(handle, value); } }
}
/// <summary>
/// Changes or gets a setting controlling the processing system behaviour. See the
/// 'SETTING_...' defines for available setting ID's.
/// </summary>
/// <value>
/// The <see cref="System.Int32"/>.
/// </value>
/// <param name="settingId">The setting identifier.</param>
/// <returns>The value of the setting</returns>
public int this[Setting settingId]
{
get
{
lock (SyncRoot) { return NativeMethods.GetSetting(handle, (int)settingId); }
}
set
{
lock (SyncRoot) { NativeMethods.SetSetting(handle, (int)settingId, value); }
}
}
#endregion
#region Sample Stream Methods
/// <summary>
/// Flushes the last samples from the processing pipeline to the output.
/// Clears also the internal processing buffers.
///
/// Note: This function is meant for extracting the last samples of a sound
/// stream. This function may introduce additional blank samples in the end
/// of the sound stream, and thus it's not recommended to call this function
/// in the middle of a sound stream.
/// </summary>
public void Flush()
{
lock (SyncRoot) { NativeMethods.Flush(handle); }
}
/// <summary>
/// Clears all the samples in the object's output and internal processing
/// buffers.
/// </summary>
public void Clear()
{
lock (SyncRoot) { NativeMethods.Clear(handle); }
}
/// <summary>
/// Adds 'numSamples' pcs of samples from the 'samples' memory position into
/// the input of the object. Notice that sample rate _has_to_ be set before
/// calling this function, otherwise throws a runtime_error exception.
/// </summary>
/// <param name="samples">Sample buffer to input</param>
/// <param name="numSamples">Number of sample frames in buffer. Notice
/// that in case of multi-channel sound a single sample frame contains
/// data for all channels</param>
public void PutSamples(float[] samples, uint numSamples)
{
lock (SyncRoot) { NativeMethods.PutSamples(handle, samples, numSamples); }
}
/// <summary>
/// int16 version of putSamples(): This accept int16 (short) sample data
/// and internally converts it to float format before processing
/// </summary>
/// <param name="samples">Sample input buffer.</param>
/// <param name="numSamples">Number of sample frames in buffer. Notice
/// that in case of multi-channel sound a single
/// sample frame contains data for all channels.</param>
public void PutSamplesI16(short[] samples, uint numSamples)
{
lock (SyncRoot) { NativeMethods.PutSamples_i16(handle, samples, numSamples); }
}
/// <summary>
/// Receive processed samples from the processor.
/// </summary>
/// <param name="outBuffer">Buffer where to copy output samples</param>
/// <param name="maxSamples">Max number of sample frames to receive</param>
/// <returns>The number of samples received</returns>
public uint ReceiveSamples(float[] outBuffer, uint maxSamples)
{
lock (SyncRoot) { return NativeMethods.ReceiveSamples(handle, outBuffer, maxSamples); }
}
/// <summary>
/// int16 version of receiveSamples(): This converts internal float samples
/// into int16 (short) return data type
/// </summary>
/// <param name="outBuffer">Buffer where to copy output samples.</param>
/// <param name="maxSamples">How many samples to receive at max.</param>
/// <returns>Number of received sample frames</returns>
public uint ReceiveSamplesI16(short[] outBuffer, uint maxSamples)
{
lock (SyncRoot) { return NativeMethods.ReceiveSamples_i16(handle, outBuffer, maxSamples); }
}
#endregion
#region IDisposable Support
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
private void Dispose(bool alsoManaged)
{
if (!IsDisposed)
{
if (alsoManaged)
{
// NOTE: Placeholder, dispose managed state (managed objects).
// At this point, nothing managed to dispose
}
NativeMethods.DestroyInstance(handle);
handle = IntPtr.Zero;
IsDisposed = true;
}
}
#endregion
#region Native Methods
/// <summary>
/// Provides direct access to mapped DLL methods
/// </summary>
private static class NativeMethods
{
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_getVersionId")]
public static extern int GetVersionId();
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_createInstance")]
public static extern IntPtr CreateInstance();
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_destroyInstance")]
public static extern void DestroyInstance(IntPtr h);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_getVersionString")]
public static extern IntPtr GetVersionString();
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setRate")]
public static extern void SetRate(IntPtr h, float newRate);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setTempo")]
public static extern void SetTempo(IntPtr h, float newTempo);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setRateChange")]
public static extern void SetRateChange(IntPtr h, float newRate);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setTempoChange")]
public static extern void SetTempoChange(IntPtr h, float newTempo);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitch")]
public static extern void SetPitch(IntPtr h, float newPitch);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitchOctaves")]
public static extern void SetPitchOctaves(IntPtr h, float newPitch);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitchSemiTones")]
public static extern void SetPitchSemiTones(IntPtr h, float newPitch);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setChannels")]
public static extern void SetChannels(IntPtr h, uint numChannels);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setSampleRate")]
public static extern void SetSampleRate(IntPtr h, uint srate);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_flush")]
public static extern void Flush(IntPtr h);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_putSamples")]
public static extern void PutSamples(IntPtr h, float[] samples, uint numSamples);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_putSamples_i16")]
public static extern void PutSamples_i16(IntPtr h, short[] samples, uint numSamples);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_clear")]
public static extern void Clear(IntPtr h);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setSetting")]
public static extern int SetSetting(IntPtr h, int settingId, int value);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_getSetting")]
public static extern int GetSetting(IntPtr h, int settingId);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_numUnprocessedSamples")]
public static extern uint NumUnprocessedSamples(IntPtr h);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_receiveSamples")]
public static extern uint ReceiveSamples(IntPtr h, float[] outBuffer, uint maxSamples);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_receiveSamples_i16")]
public static extern uint ReceiveSamples_i16(IntPtr h, short[] outBuffer, uint maxSamples);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_numSamples")]
public static extern uint NumSamples(IntPtr h);
[DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_isEmpty")]
public static extern int IsEmpty(IntPtr h);
}
#endregion
}
public sealed class BPMDetect : IDisposable
{
#region Private Members
private readonly object SyncRoot = new object();
private bool IsDisposed = false;
private IntPtr handle;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="BPMDetect"/> class.
/// </summary>
public BPMDetect(int numChannels, int sampleRate)
{
handle = NativeMethods.BpmCreateInstance(numChannels, sampleRate);
}
/// <summary>
/// Finalizes an instance of the <see cref="BPMDetect"/> class.
/// </summary>
~BPMDetect()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
#endregion
#region Properties
/// <summary>
/// Returns the analysed BPM rate.
/// </summary>
public float Bpm
{
get { lock (SyncRoot) { return NativeMethods.BpmGet(handle); } }
}
#endregion
#region Sample Stream Methods
/// <summary>
/// Feed 'numSamples' sample into the BPM detector
/// </summary>
/// <param name="samples">Sample buffer to input</param>
/// <param name="numSamples">Number of sample frames in buffer. Notice
/// that in case of multi-channel sound a single sample frame contains
/// data for all channels</param>
public void PutSamples(float[] samples, uint numSamples)
{
lock (SyncRoot) { NativeMethods.BpmPutSamples(handle, samples, numSamples); }
}
/// <summary>
/// int16 version of putSamples(): This accept int16 (short) sample data
/// and internally converts it to float format before processing
/// </summary>
/// <param name="samples">Sample input buffer.</param>
/// <param name="numSamples">Number of sample frames in buffer. Notice
/// that in case of multi-channel sound a single
/// sample frame contains data for all channels.</param>
public void PutSamplesI16(short[] samples, uint numSamples)
{
lock (SyncRoot) { NativeMethods.BpmPutSamples_i16(handle, samples, numSamples); }
}
#endregion
#region IDisposable Support
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
private void Dispose(bool alsoManaged)
{
if (!IsDisposed)
{
if (alsoManaged)
{
// NOTE: Placeholder, dispose managed state (managed objects).
// At this point, nothing managed to dispose
}
NativeMethods.BpmDestroyInstance(handle);
handle = IntPtr.Zero;
IsDisposed = true;
}
}
#endregion
#region Native Methods
/// <summary>
/// Provides direct access to mapped DLL methods
/// </summary>
private static class NativeMethods
{
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_createInstance")]
public static extern IntPtr BpmCreateInstance(int numChannels, int sampleRate);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_destroyInstance")]
public static extern void BpmDestroyInstance(IntPtr h);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_putSamples")]
public static extern void BpmPutSamples(IntPtr h, float[] samples, uint numSamples);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_putSamples_i16")]
public static extern void BpmPutSamples_i16(IntPtr h, short[] samples, uint numSamples);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_getBpm")]
public static extern float BpmGet(IntPtr h);
}
#endregion
}
}

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1BE6EF4F-6A62-447B-A863-E918D68BDAD7}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>csharp_example</RootNamespace>
<AssemblyName>csharp-example</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="NAudio, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>lib\NAudio.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="SoundProcessor.cs" />
<Compile Include="SoundTouch.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="NAudio.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="SoundTouch.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,22 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csharp-example", "csharp-example.csproj", "{1BE6EF4F-6A62-447B-A863-E918D68BDAD7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1BE6EF4F-6A62-447B-A863-E918D68BDAD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1BE6EF4F-6A62-447B-A863-E918D68BDAD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1BE6EF4F-6A62-447B-A863-E918D68BDAD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1BE6EF4F-6A62-447B-A863-E918D68BDAD7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal