1
0
mirror of https://github.com/RPCS3/soundtouch.git synced 2024-11-08 12:02:28 +01:00

Refactored C# interface & example

This commit is contained in:
oparviai 2017-10-30 16:53:17 +00:00
parent 80281c8e1b
commit 05a3403137
5 changed files with 492 additions and 265 deletions

View File

@ -13,7 +13,7 @@
</head>
<body class="normal">
<hr>
<h1>SoundTouch audio processing library v2.0</h1>
<h1>SoundTouch audio processing library v2.0.1pre</h1>
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2017</p>
<hr>
<h2>1. Introduction </h2>
@ -123,7 +123,7 @@ destination locations.</p>
</tr>
</tbody>
</table>
<h4><b>2.2.1 Required GNU tools</b>&nbsp;</h4>
<h4><b>2.2.1 Required GNU tools</b></h4>
<p> <span style="font-weight: bold;">Bash shell</span>, <span
style="font-weight: bold;">GNU C++ compiler</span>, <span
style="font-weight: bold;">libtool</span>, <span
@ -222,7 +222,7 @@ as combination of two primary effects, <em>sample rate transposing</em>
and <em>time-stretching</em>.</p>
<p><em>Sample rate transposing</em> affects both the audio stream
duration and pitch. It's implemented simply by converting the original
audio sample stream to the&nbsp; 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.&nbsp;<br>
when increasing the tempo and vice versa.<br>
<br>
By default, this setting value is calculated automatically according to
tempo value.<br>
@ -280,7 +280,7 @@ tempo value.<br>
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.&nbsp;<br>
the sound sequences are to be linked back together.<br>
<br>
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</td>
</td>
<td valign="top">Default value is relatively large, chosen to
suit with above parameters.</td>
<td valign="top">&nbsp;</td>
<td valign="top"></td>
<td valign="top">If you reduce the "sequence ms" setting, you
might wish to try a smaller value.</td>
<td valign="top">Increasing the parameter value increases
@ -361,7 +361,7 @@ computation burden</td>
<p>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&nbsp; id of SETTING_USE_QUICKSEEK and value
function with parameter id of SETTING_USE_QUICKSEEK and value
"1", i.e. </p>
<blockquote>
<p>setSetting(SETTING_USE_QUICKSEEK, 1);</p>
@ -445,13 +445,13 @@ file format). Give "stdin" as filename to use standard input pipe. </td>
</td>
<td valign="top">Name of the output sound file where the
resulting sound is saved (in .WAV audio file format). This parameter
may be omitted if you&nbsp; 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.</td>
</tr>
<tr>
<td valign="top">
<pre>&nbsp;[switches]</pre>
<pre>[switches]</pre>
</td>
<td valign="top">Are one or more control switches.</td>
</tr>
@ -575,10 +575,11 @@ this corresponds to lowering the pitch by -0.318 semitones:</p>
<h3>5.1. SoundTouch library Change History </h3>
<p><b>2.0.1pre:</b></p>
<ul>
<li>Refactored C# interface example</li>
<li>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</li>
SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias
filter cause slight click if the rate change crosses zero during
processing</li>
</ul>
<p><b>2.0:</b></p>
<ul>
@ -670,13 +671,13 @@ sample batch sizes</li>
<ul>
<li> Added normalization to correlation calculation and improvement
automatic seek/sequence parameter calculation to improve sound quality</li>
<li> Bugfixes:&nbsp;
<li> Bugfixes:
<ul>
<li> Fixed negative array indexing in quick seek algorithm</li>
<li> FIR autoalias filter running too far in processing buffer</li>
<li> Check against zero sample count in rate transposing</li>
<li> Fix for x86-64 support: Removed pop/push instructions from
the cpu detection algorithm.&nbsp;</li>
the cpu detection algorithm.</li>
<li> Check against empty buffers in FIFOSampleBuffer</li>
<li> Other minor fixes &amp; code cleanup</li>
</ul>
@ -692,7 +693,7 @@ negative side or vice versa</li>
<p><strong>1.4.1:</strong></p>
<ul>
<li> Fixed a buffer overflow bug in BPM detect algorithm routines if
processing more than 2048 samples at one call&nbsp;</li>
processing more than 2048 samples at one call</li>
</ul>
<p><strong>1.4.0:</strong></p>
<ul>
@ -775,7 +776,6 @@ accessing the FIFOSampleBuffer class from external files.</li>
<ul>
<li> Initial release</li>
</ul>
<p>&nbsp;</p>
<h3>5.2. SoundStretch application Change History </h3>
<p><b>1.9:</b></p>
<ul>
@ -862,7 +862,7 @@ submitted bugfixes:</p>
<li> Jason Garland</li>
<li> Takashi Iwai</li>
<li> Thomas Klausner</li>
<li> Lu Zhihe</li>
<li> Lu Zhihe</li>
<li> Tony Mechelynck </li>
<li> Mathias M&ouml;hl</li>
<li> Yuval Naveh</li>
@ -879,8 +879,9 @@ submitted bugfixes:</p>
<li> Albert Sirvent</li>
<li> Tyson Smith</li>
<li> John Stumpo</li>
<li> Mario di Vece</li>
<li> Katja Vetter</li>
<li> Wu Q.</li>
<li> Wu Q.</li>
</ul>
<p>Moral greetings to all other contributors and users also!</p>
<hr>

View File

@ -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">
<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"/>

View File

@ -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]);
}
}
}

View File

@ -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;
}
/// <summary>
@ -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)

View File

@ -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
/// <summary>
/// Initializes a new instance of the <see cref="SoundTouch"/> class.
/// </summary>
public SoundTouch()
{
handle = soundtouch_createInstance();
handle = NativeMethods.CreateInstance();
}
/// <summary>
/// Finalizes an instance of the <see cref="SoundTouch"/> class.
/// </summary>
~SoundTouch()
{
soundtouch_destroyInstance(handle);
// 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 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());
}
}
/// <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 NumSamples()
public uint AvailableSampleCount
{
return soundtouch_numSamples(handle);
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>
@ -78,274 +384,161 @@ namespace soundtouch
/// data for all channels</param>
public void PutSamples(float[] samples, uint numSamples)
{
soundtouch_putSamples(handle, samples, numSamples);
}
/// <summary>
/// Sets the number of channels
/// </summary>
/// <param name="numChannels">1 = mono, 2 = stereo, n = multichannel</param>
public void SetChannels(uint numChannels)
{
soundtouch_setChannels(handle, numChannels);
}
/// <summary>
/// Sets sample rate.
/// </summary>
/// <param name="srate">Samplerate, e.g. 44100</param>
public void SetSampleRate(uint srate)
{
soundtouch_setSampleRate(handle, srate);
}
/// <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></returns>
public uint ReceiveSamples(float[] outBuffer, uint maxSamples)
{
return soundtouch_receiveSamples(handle, outBuffer, maxSamples);
lock (SyncRoot) { NativeMethods.PutSamples(handle, samples, numSamples); }
}
/// <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()
{
soundtouch_flush(handle);
}
/// <summary>
/// Clears all the samples in the object's output and internal processing
/// buffers.
/// </summary>
public void Clear()
{
soundtouch_clear(handle);
}
/// <summary>
/// Sets new tempo control value.
/// </summary>
/// <param name="newTempo">Tempo setting. Normal tempo = 1.0, smaller values
/// represent slower tempo, larger faster tempo.</param>
public void SetTempo(float newTempo)
{
soundtouch_setTempo(handle, newTempo);
}
/// <summary>
/// Sets new tempo control value as a difference in percents compared
/// to the original tempo (-50 .. +100 %);
/// </summary>
/// <param name="newTempo">Tempo setting in %</param>
public void SetTempoChange(float newTempo)
{
soundtouch_setTempoChange(handle, newTempo);
}
/// <summary>
/// Sets new rate control value.
/// </summary>
/// <param name="newRate">Rate setting. Normal rate = 1.0, smaller values
/// represent slower rate, larger faster rate.</param>
public void SetRate(float newRate)
{
soundtouch_setTempo(handle, newRate);
}
/// <summary>
/// Sets new rate control value as a difference in percents compared
/// to the original rate (-50 .. +100 %);
/// </summary>
/// <param name="newRate">Rate setting in %</param>
public void SetRateChange(float newRate)
{
soundtouch_setRateChange(handle, newRate);
}
/// <summary>
/// Sets new pitch control value.
/// </summary>
/// <param name="newPitch">Pitch setting. Original pitch = 1.0, smaller values
/// represent lower pitches, larger values higher pitch.</param>
public void SetPitch(float newPitch)
{
soundtouch_setPitch(handle, newPitch);
}
/// <summary>
/// Sets pitch change in octaves compared to the original pitch
/// (-1.00 .. +1.00 for +- one octave);
/// </summary>
/// <param name="newPitch">Pitch setting in octaves</param>
public void SetPitchOctaves(float newPitch)
{
soundtouch_setPitchOctaves(handle, newPitch);
}
/// <summary>
/// Sets pitch change in semi-tones compared to the original pitch
/// (-12 .. +12 for +- one octave);
/// </summary>
/// <param name="newPitch">Pitch setting in semitones</param>
public void SetPitchSemiTones(float newPitch)
{
soundtouch_setPitchSemiTones(handle, newPitch);
}
/// <summary>
/// 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
/// </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 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); }
}
/// <summary>
/// Changes a setting controlling the processing system behaviour. See the
/// 'SETTING_...' defines for available setting ID's.
/// Receive processed samples from the processor.
/// </summary>
/// <param name="settingId">Setting ID number. see SETTING_... defines.</param>
/// <param name="value"New setting value></param>
/// <returns>nonzero if successful, otherwise zero</returns>
public int SetSetting(int settingId, int value)
/// <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)
{
return soundtouch_setSetting(handle, settingId, value);
lock (SyncRoot) { return NativeMethods.ReceiveSamples(handle, outBuffer, maxSamples); }
}
/// <summary>
/// Reads a setting controlling the processing system behaviour. See the
/// 'SETTING_...' defines for available setting ID's.
/// </summary>
/// <param name="settingId">Setting ID number</param>
/// <returns>The setting value</returns>
public int soundtouch_getSetting(int settingId)
{
return soundtouch_getSetting(handle, settingId);
}
/// <summary>
/// Returns number of samples currently unprocessed in SoundTouch internal buffer
/// </summary>
/// <returns>Number of sample frames</returns>
public uint NumUnprocessedSamples()
{
return soundtouch_numUnprocessedSamples(handle);
}
/// <summary>
/// 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
/// </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 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
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Check if there aren't any samples available for outputting.
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <returns>nonzero if there aren't any samples available for outputting</returns>
public int IsEmpty()
/// <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)
{
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
/// <summary>
/// Get SoundTouch library version Id
/// Provides direct access to mapped DLL methods
/// </summary>
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
}
}
}