From 05a3403137d513f7b6ed839e9429f5505dbd749d Mon Sep 17 00:00:00 2001
From: oparviai SoundTouch library Copyright © Olli Parviainen 2001-2017
-SoundTouch audio processing library v2.0
+SoundTouch audio processing library v2.0.1pre
1. Introduction
@@ -123,7 +123,7 @@ destination locations.
Bash shell, GNU C++ compiler, libtool, sample rate transposing and time-stretching.
Sample rate transposing affects both the audio stream
duration and pitch. It's implemented simply by converting the original
-audio sample stream to the desired duration by interpolating from
+audio sample stream to the desired duration by interpolating from
the original audio samples. In SoundTouch, linear interpolation with
anti-alias filtering is used. Theoretically a higher-order
interpolation provide better result than 1st order linear
@@ -271,7 +271,7 @@ length of a single processing sequence in milliseconds which determines
the how the original sound is chopped in the time-stretch algorithm.
Larger values mean fewer sequences are used in processing. In principle
a larger value sounds better when slowing down the tempo, but worse
-when increasing the tempo and vice versa.
+when increasing the tempo and vice versa.
By default, this setting value is calculated automatically according to
tempo value.
@@ -280,7 +280,7 @@ tempo value.
default length in milliseconds is for the algorithm that seeks the best
possible overlapping location. This determines from how wide a sample
"window" the algorithm can use to find an optimal mixing location when
-the sound sequences are to be linked back together.
+the sound sequences are to be linked back together.
The bigger this window setting is, the higher the possibility to find a
better mixing position becomes, but at the same time large values may
@@ -348,7 +348,7 @@ computation burden
The time-stretch routine has a 'quick' mode that substantially speeds up the algorithm but may slightly compromise the sound quality. This mode is activated by calling SoundTouch::setSetting() -function with parameter id of SETTING_USE_QUICKSEEK and value +function with parameter id of SETTING_USE_QUICKSEEK and value "1", i.e.
setSetting(SETTING_USE_QUICKSEEK, 1);
@@ -445,13 +445,13 @@ file format). Give "stdin" as filename to use standard input pipe.Name of the output sound file where the resulting sound is saved (in .WAV audio file format). This parameter -may be omitted if you don't want to save the output (e.g. when +may be omitted if you don't want to save the output (e.g. when only calculating BPM rate with '-bpm' switch). Give "stdout" as filename to use standard output pipe. @@ -575,10 +575,11 @@ this corresponds to lowering the pitch by -0.318 semitones: - [switches]+[switches]Are one or more control switches. 5.1. SoundTouch library Change History
2.0.1pre:
+
- Refactored C# interface example
- Disable anti-alias filter when switch - SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias - filter cause slight click if the rate change crosses zero during - processing
+ SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias + filter cause slight click if the rate change crosses zero during + processing2.0:
@@ -670,13 +671,13 @@ sample batch sizes
@@ -692,7 +693,7 @@ negative side or vice versa
- Added normalization to correlation calculation and improvement automatic seek/sequence parameter calculation to improve sound quality
-- Bugfixes: +
- Bugfixes:
- Fixed negative array indexing in quick seek algorithm
- FIR autoalias filter running too far in processing buffer
- Check against zero sample count in rate transposing
- Fix for x86-64 support: Removed pop/push instructions from -the cpu detection algorithm.
+the cpu detection algorithm.- Check against empty buffers in FIFOSampleBuffer
- Other minor fixes & code cleanup
1.4.1:
- Fixed a buffer overflow bug in BPM detect algorithm routines if -processing more than 2048 samples at one call
+processing more than 2048 samples at one call1.4.0:
@@ -775,7 +776,6 @@ accessing the FIFOSampleBuffer class from external files.
-
- Initial release
5.2. SoundStretch application Change History
1.9:
@@ -862,7 +862,7 @@ submitted bugfixes:
- Jason Garland
- Takashi Iwai
- Thomas Klausner
-- Lu Zhihe
+- Lu Zhihe
- Tony Mechelynck
- Mathias Möhl
- Yuval Naveh
@@ -879,8 +879,9 @@ submitted bugfixes:- Albert Sirvent
- Tyson Smith
- John Stumpo
+- Mario di Vece
- Katja Vetter
-- Wu Q.
+- Wu Q.
Moral greetings to all other contributors and users also!
diff --git a/source/csharp-example/MainWindow.xaml b/source/csharp-example/MainWindow.xaml index cd9af03..1cad298 100644 --- a/source/csharp-example/MainWindow.xaml +++ b/source/csharp-example/MainWindow.xaml @@ -5,7 +5,7 @@ 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"> + Title="C# SoundTouch example" Height="250" Width="400" AllowDrop="True" Drop="Window_Drop">diff --git a/source/csharp-example/MainWindow.xaml.cs b/source/csharp-example/MainWindow.xaml.cs index 4e844b9..5028cf4 100644 --- a/source/csharp-example/MainWindow.xaml.cs +++ b/source/csharp-example/MainWindow.xaml.cs @@ -14,6 +14,7 @@ using soundtouch; using System; +using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -45,7 +46,7 @@ namespace csharp_example string status; try { - status = String.Format("SoundTouch version: {0}", SoundTouch.GetVersionString()); + status = String.Format("SoundTouch version: {0}", SoundTouch.Version); } catch (Exception exp) { @@ -61,27 +62,44 @@ namespace csharp_example } + // 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) { - if (processor.OpenMp3File(openDialog.FileName) == true) - { - textBox_filename.Text = openDialog.FileName; - button_play.IsEnabled = true; - button_stop.IsEnabled = true; - - // Parse adjustment settings - ParseTempoTextBox(); - ParsePitchTextBox(); - ParseRateTextBox(); - } - else - { - MessageBox.Show("Coudln't open audio file " + openDialog.FileName); - } + OpenFile(openDialog.FileName); } } @@ -105,7 +123,7 @@ namespace csharp_example private void button_play_Click(object sender, RoutedEventArgs e) { - if (button_play.Content == "_Pause") + if ((string)button_play.Content == "_Pause") { // Pause if (processor.Pause()) @@ -126,7 +144,7 @@ namespace csharp_example } - private void button_stop_Click(object sender, RoutedEventArgs e) + private void Stop() { if (processor.Stop()) { @@ -136,6 +154,12 @@ namespace csharp_example } + 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; @@ -150,7 +174,7 @@ namespace csharp_example double pitchValue; if (double.TryParse(textBox_pitch.Text, out pitchValue)) { - if (processor.streamProcessor != null) processor.streamProcessor.st.SetPitchSemiTones((float)pitchValue); + if (processor.streamProcessor != null) processor.streamProcessor.st.PitchSemiTones = (float)pitchValue; } } @@ -160,7 +184,7 @@ namespace csharp_example double tempoValue; if (parse_percentValue(textBox_tempo, out tempoValue)) { - if (processor.streamProcessor != null) processor.streamProcessor.st.SetTempoChange((float)tempoValue); + if (processor.streamProcessor != null) processor.streamProcessor.st.TempoChange = (float)tempoValue; } } @@ -170,7 +194,7 @@ namespace csharp_example double rateValue; if (parse_percentValue(textBox_rate, out rateValue)) { - if (processor.streamProcessor != null) processor.streamProcessor.st.SetRateChange((float)rateValue); + if (processor.streamProcessor != null) processor.streamProcessor.st.RateChange = (float)rateValue; } } @@ -221,5 +245,14 @@ namespace csharp_example 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]); + } } } diff --git a/source/csharp-example/SoundProcessor.cs b/source/csharp-example/SoundProcessor.cs index 8c8665f..7bc2a70 100644 --- a/source/csharp-example/SoundProcessor.cs +++ b/source/csharp-example/SoundProcessor.cs @@ -64,8 +64,8 @@ namespace csharp_example { inputStr = input; st = new SoundTouch(); - st.SetChannels((uint)input.WaveFormat.Channels); - st.SetSampleRate((uint)input.WaveFormat.SampleRate); + st.Channels = (uint)input.WaveFormat.Channels; + st.SampleRate = (uint)input.WaveFormat.SampleRate; } /// @@ -123,7 +123,7 @@ namespace csharp_example // Iterate until enough samples available for output: // - read samples from input stream // - put samples to SoundStretch processor - while (st.NumSamples() < count) + while (st.AvailableSampleCount < count) { int nbytes = inputStr.Read(bytebuffer, 0, bytebuffer.Length); if (nbytes == 0) diff --git a/source/csharp-example/SoundTouch.cs b/source/csharp-example/SoundTouch.cs index bdf4529..9682615 100644 --- a/source/csharp-example/SoundTouch.cs +++ b/source/csharp-example/SoundTouch.cs @@ -6,6 +6,8 @@ /// Author e-mail : oparviai 'at' iki.fi /// SoundTouch WWW: http://www.surina.net/soundtouch /// +/// The C# wrapper improved by Mario Di Vece +/// //////////////////////////////////////////////////////////////////////////////// // // License : @@ -34,37 +36,341 @@ using System.Runtime.InteropServices; namespace soundtouch { - public class SoundTouch + public sealed class SoundTouch : IDisposable { + #region Private Members + + private const string SoundTouchLibrary = "SoundTouch.dll"; + private readonly object SyncRoot = new object(); + private bool IsDisposed = false; private IntPtr handle; + #endregion + + #region Constructor + + /// + /// Initializes a new instance of the public SoundTouch() { - handle = soundtouch_createInstance(); + handle = NativeMethods.CreateInstance(); } - + ///class. + /// + /// Finalizes an instance of the ~SoundTouch() { - soundtouch_destroyInstance(handle); + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); } + ///class. + /// + /// Settings as defined in SoundTouch.h + /// + public enum Setting + { + ///+ /// Enable/disable anti-alias filter in pitch transposer (0 = disable) + /// + UseAntiAliasFilter = 0, + + ///+ /// Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32) + /// + AntiAliasFilterLength = 1, + + ///+ /// Enable/disable quick seeking algorithm in tempo changer routine + /// (enabling quick seeking lowers CPU utilization but causes a minor sound + /// quality compromising) + /// + UseQuickSeek = 2, + + ///+ /// 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. + /// + SequenceMilliseconds = 3, + + ///+ /// 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. + /// + SeekWindowMilliseconds = 4, + + ///+ /// 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. + /// + OverlapMilliseconds = 5, + + ///+ /// 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. + /// + NominalInputSequence = 6, + + ///+ /// 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. + /// + NominalOutputSequence = 7, + + ///+ /// 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. + /// + InitialLatency = 8, + } + + #endregion + + #region Properties + ////// Get SoundTouch version string /// - public static String GetVersionString() + public static string Version { - // convert "char *" data to c# string - return Marshal.PtrToStringAnsi(soundtouch_getVersionString()); + get + { + // convert "char *" data to c# string + return Marshal.PtrToStringAnsi(NativeMethods.GetVersionString()); + } } + ///+ /// Gets a value indicating whether the SoundTouch Library (dll) is available + /// + public static bool IsAvailable + { + get + { + try + { + var versionId = NativeMethods.GetVersionId(); + return versionId != 0; + } + catch + { + return false; + } + } + } ////// Returns number of processed samples currently available in SoundTouch for immediate output. /// - public uint NumSamples() + public uint AvailableSampleCount { - return soundtouch_numSamples(handle); + get { lock (SyncRoot) { return NativeMethods.NumSamples(handle); } } + } + + ///+ /// Returns number of samples currently unprocessed in SoundTouch internal buffer + /// + ///Number of sample frames + public uint UnprocessedSampleCount + { + get { lock (SyncRoot) { return NativeMethods.NumUnprocessedSamples(handle); } } + } + + ///+ /// Check if there aren't any samples available for outputting. + /// + ///nonzero if there aren't any samples available for outputting + public int IsEmpty + { + get { lock (SyncRoot) { return NativeMethods.IsEmpty(handle); } } + } + + ///+ /// Sets the number of channels + /// + /// Value: 1 = mono, 2 = stereo, n = multichannel + /// + public uint Channels + { + set { lock (SyncRoot) { NativeMethods.SetChannels(handle, value); } } + } + + ///+ /// Sets sample rate. + /// Value: Sample rate, e.g. 44100 + /// + public uint SampleRate + { + set { lock (SyncRoot) { NativeMethods.SetSampleRate(handle, value); } } + } + + ///+ /// Sets new tempo control value. + /// + /// Value: Tempo setting. Normal tempo = 1.0, smaller values + /// represent slower tempo, larger faster tempo. + /// + public float Tempo + { + set { lock (SyncRoot) { NativeMethods.SetTempo(handle, value); } } + } + + ///+ /// Sets new tempo control value as a difference in percents compared + /// to the original tempo (-50 .. +100 %); + /// + public float TempoChange + { + set { lock (SyncRoot) { NativeMethods.SetTempoChange(handle, value); } } + } + + ///+ /// Sets new rate control value. + /// Rate setting. Normal rate = 1.0, smaller values + /// represent slower rate, larger faster rate. + /// + public float Rate + { + set { lock (SyncRoot) { NativeMethods.SetTempo(handle, value); } } + } + + ///+ /// Sets new rate control value as a difference in percents compared + /// to the original rate (-50 .. +100 %); + /// + /// Value: Rate setting is in % + /// + public float RateChange + { + set { lock (SyncRoot) { NativeMethods.SetRateChange(handle, value); } } + } + + ///+ /// Sets new pitch control value. + /// + /// Value: Pitch setting. Original pitch = 1.0, smaller values + /// represent lower pitches, larger values higher pitch. + /// + public float Pitch + { + set { lock (SyncRoot) { NativeMethods.SetPitch(handle, value); } } + } + + ///+ /// Sets pitch change in octaves compared to the original pitch + /// (-1.00 .. +1.00 for +- one octave); + /// + /// Value: Pitch setting in octaves + /// + public float PitchOctaves + { + set { lock (SyncRoot) { NativeMethods.SetPitchOctaves(handle, value); } } + } + + ///+ /// Sets pitch change in semi-tones compared to the original pitch + /// (-12 .. +12 for +- one octave); + /// + /// Value: Pitch setting in semitones + /// + public float PitchSemiTones + { + set { lock (SyncRoot) { NativeMethods.SetPitchSemiTones(handle, value); } } + } + + ///+ /// Changes or gets a setting controlling the processing system behaviour. See the + /// 'SETTING_...' defines for available setting ID's. + /// + ///+ /// The + /// The setting identifier. + ///. + /// The value of the setting + 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 + + ///+ /// 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. + /// + public void Flush() + { + lock (SyncRoot) { NativeMethods.Flush(handle); } + } + + ///+ /// Clears all the samples in the object's output and internal processing + /// buffers. + /// + public void Clear() + { + lock (SyncRoot) { NativeMethods.Clear(handle); } } ///@@ -78,274 +384,161 @@ namespace soundtouch /// data for all channels public void PutSamples(float[] samples, uint numSamples) { - soundtouch_putSamples(handle, samples, numSamples); - } - - - /// - /// Sets the number of channels - /// - /// 1 = mono, 2 = stereo, n = multichannel - public void SetChannels(uint numChannels) - { - soundtouch_setChannels(handle, numChannels); - } - - - ///- /// Sets sample rate. - /// - /// Samplerate, e.g. 44100 - public void SetSampleRate(uint srate) - { - soundtouch_setSampleRate(handle, srate); - } - - - ///- /// Receive processed samples from the processor. - /// - /// Buffer where to copy output samples - /// Max number of sample frames to receive - ///- public uint ReceiveSamples(float[] outBuffer, uint maxSamples) - { - return soundtouch_receiveSamples(handle, outBuffer, maxSamples); + lock (SyncRoot) { NativeMethods.PutSamples(handle, samples, numSamples); } } /// - /// 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. - /// - public void Flush() - { - soundtouch_flush(handle); - } - - ///- /// Clears all the samples in the object's output and internal processing - /// buffers. - /// - public void Clear() - { - soundtouch_clear(handle); - } - - ///- /// Sets new tempo control value. - /// - /// Tempo setting. Normal tempo = 1.0, smaller values - /// represent slower tempo, larger faster tempo. - public void SetTempo(float newTempo) - { - soundtouch_setTempo(handle, newTempo); - } - - ///- /// Sets new tempo control value as a difference in percents compared - /// to the original tempo (-50 .. +100 %); - /// - /// Tempo setting in % - public void SetTempoChange(float newTempo) - { - soundtouch_setTempoChange(handle, newTempo); - } - - ///- /// Sets new rate control value. - /// - /// Rate setting. Normal rate = 1.0, smaller values - /// represent slower rate, larger faster rate. - public void SetRate(float newRate) - { - soundtouch_setTempo(handle, newRate); - } - - ///- /// Sets new rate control value as a difference in percents compared - /// to the original rate (-50 .. +100 %); - /// - /// Rate setting in % - public void SetRateChange(float newRate) - { - soundtouch_setRateChange(handle, newRate); - } - - ///- /// Sets new pitch control value. - /// - /// Pitch setting. Original pitch = 1.0, smaller values - /// represent lower pitches, larger values higher pitch. - public void SetPitch(float newPitch) - { - soundtouch_setPitch(handle, newPitch); - } - - ///- /// Sets pitch change in octaves compared to the original pitch - /// (-1.00 .. +1.00 for +- one octave); - /// - /// Pitch setting in octaves - public void SetPitchOctaves(float newPitch) - { - soundtouch_setPitchOctaves(handle, newPitch); - } - - ///- /// Sets pitch change in semi-tones compared to the original pitch - /// (-12 .. +12 for +- one octave); - /// - /// Pitch setting in semitones - public void SetPitchSemiTones(float newPitch) - { - soundtouch_setPitchSemiTones(handle, newPitch); - } - - ///- /// int16 version of soundtouch_putSamples(): This accept int16 (short) sample data + /// int16 version of putSamples(): This accept int16 (short) sample data /// and internally converts it to float format before processing /// /// Sample input buffer. /// Number of sample frames in buffer. Notice /// that in case of multi-channel sound a single /// sample frame contains data for all channels. - public void PutSamples_i16(short[] samples, uint numSamples) + public void PutSamplesI16(short[] samples, uint numSamples) { - soundtouch_putSamples_i16(handle, samples, numSamples); + lock (SyncRoot) { NativeMethods.PutSamples_i16(handle, samples, numSamples); } } ///- /// Changes a setting controlling the processing system behaviour. See the - /// 'SETTING_...' defines for available setting ID's. + /// Receive processed samples from the processor. /// - /// Setting ID number. see SETTING_... defines. - /// - ///nonzero if successful, otherwise zero - public int SetSetting(int settingId, int value) + /// Buffer where to copy output samples + /// Max number of sample frames to receive + ///The number of samples received + public uint ReceiveSamples(float[] outBuffer, uint maxSamples) { - return soundtouch_setSetting(handle, settingId, value); + lock (SyncRoot) { return NativeMethods.ReceiveSamples(handle, outBuffer, maxSamples); } } ///- /// Reads a setting controlling the processing system behaviour. See the - /// 'SETTING_...' defines for available setting ID's. - /// - /// Setting ID number - ///The setting value - public int soundtouch_getSetting(int settingId) - { - return soundtouch_getSetting(handle, settingId); - } - - ///- /// Returns number of samples currently unprocessed in SoundTouch internal buffer - /// - ///Number of sample frames - public uint NumUnprocessedSamples() - { - return soundtouch_numUnprocessedSamples(handle); - } - - ///- /// int16 version of soundtouch_receiveSamples(): This converts internal float samples + /// int16 version of receiveSamples(): This converts internal float samples /// into int16 (short) return data type /// /// Buffer where to copy output samples. /// How many samples to receive at max. ///Number of received sample frames - public uint soundtouch_receiveSamples_i16(short[] outBuffer, uint maxSamples) + public uint ReceiveSamplesI16(short[] outBuffer, uint maxSamples) { - return soundtouch_receiveSamples_i16(handle, outBuffer, maxSamples); + lock (SyncRoot) { return NativeMethods.ReceiveSamples_i16(handle, outBuffer, maxSamples); } + } + + #endregion + + #region IDisposable Support + + ///+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } ///- /// Check if there aren't any samples available for outputting. + /// Releases unmanaged and - optionally - managed resources. /// - ///nonzero if there aren't any samples available for outputting - public int IsEmpty() + ///true to release both managed and unmanaged resources;false to release only unmanaged resources. + private void Dispose(bool alsoManaged) { - return soundtouch_isEmpty(handle); + 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; + } } - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_getVersionId")] + #endregion + + #region Native Methods + ///- /// Get SoundTouch library version Id + /// Provides direct access to mapped DLL methods /// - public static extern int GetVersionId(); + 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("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr soundtouch_createInstance(); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_destroyInstance")] + public static extern void DestroyInstance(IntPtr h); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_destroyInstance(IntPtr h); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_getVersionString")] + public static extern IntPtr GetVersionString(); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr soundtouch_getVersionString(); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setRate")] + public static extern void SetRate(IntPtr h, float newRate); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setRate(IntPtr h, float newRate); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setTempo")] + public static extern void SetTempo(IntPtr h, float newTempo); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setTempo(IntPtr h, float newTempo); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setRateChange")] + public static extern void SetRateChange(IntPtr h, float newRate); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setRateChange(IntPtr h, float newRate); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setTempoChange")] + public static extern void SetTempoChange(IntPtr h, float newTempo); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setTempoChange(IntPtr h, float newTempo); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitch")] + public static extern void SetPitch(IntPtr h, float newPitch); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setPitch(IntPtr h, float newPitch); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitchOctaves")] + public static extern void SetPitchOctaves(IntPtr h, float newPitch); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setPitchOctaves(IntPtr h, float newPitch); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitchSemiTones")] + public static extern void SetPitchSemiTones(IntPtr h, float newPitch); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setPitchSemiTones(IntPtr h, float newPitch); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setChannels")] + public static extern void SetChannels(IntPtr h, uint numChannels); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setChannels(IntPtr h, uint numChannels); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setSampleRate")] + public static extern void SetSampleRate(IntPtr h, uint srate); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_setSampleRate(IntPtr h, uint srate); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_flush")] + public static extern void Flush(IntPtr h); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_flush(IntPtr h); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_putSamples")] + public static extern void PutSamples(IntPtr h, float[] samples, uint numSamples); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_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("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_putSamples_i16(IntPtr h, short[] samples, uint numSamples); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_clear")] + public static extern void Clear(IntPtr h); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern void soundtouch_clear(IntPtr h); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setSetting")] + public static extern int SetSetting(IntPtr h, int settingId, int value); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern int soundtouch_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("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern int soundtouch_getSetting(IntPtr h, int settingId); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_numUnprocessedSamples")] + public static extern uint NumUnprocessedSamples(IntPtr h); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern uint soundtouch_numUnprocessedSamples(IntPtr h); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_receiveSamples")] + public static extern uint ReceiveSamples(IntPtr h, float[] outBuffer, uint maxSamples); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern uint soundtouch_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("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern uint soundtouch_receiveSamples_i16(IntPtr h, short[] outBuffer, uint maxSamples); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_numSamples")] + public static extern uint NumSamples(IntPtr h); - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern uint soundtouch_numSamples(IntPtr h); + [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_isEmpty")] + public static extern int IsEmpty(IntPtr h); + } - [DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] - private static extern int soundtouch_isEmpty(IntPtr h); + #endregion } -} +} \ No newline at end of file