1
0
mirror of https://github.com/RPCS3/soundtouch.git synced 2024-11-08 20:12:27 +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> </head>
<body class="normal"> <body class="normal">
<hr> <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> <p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2017</p>
<hr> <hr>
<h2>1. Introduction </h2> <h2>1. Introduction </h2>
@ -123,7 +123,7 @@ destination locations.</p>
</tr> </tr>
</tbody> </tbody>
</table> </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 <p> <span style="font-weight: bold;">Bash shell</span>, <span
style="font-weight: bold;">GNU C++ compiler</span>, <span style="font-weight: bold;">GNU C++ compiler</span>, <span
style="font-weight: bold;">libtool</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> and <em>time-stretching</em>.</p>
<p><em>Sample rate transposing</em> affects both the audio stream <p><em>Sample rate transposing</em> affects both the audio stream
duration and pitch. It's implemented simply by converting the original 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 the original audio samples. In SoundTouch, linear interpolation with
anti-alias filtering is used. Theoretically a higher-order anti-alias filtering is used. Theoretically a higher-order
interpolation provide better result than 1st order linear 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. the how the original sound is chopped in the time-stretch algorithm.
Larger values mean fewer sequences are used in processing. In principle Larger values mean fewer sequences are used in processing. In principle
a larger value sounds better when slowing down the tempo, but worse 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> <br>
By default, this setting value is calculated automatically according to By default, this setting value is calculated automatically according to
tempo value.<br> tempo value.<br>
@ -280,7 +280,7 @@ tempo value.<br>
default length in milliseconds is for the algorithm that seeks the best default length in milliseconds is for the algorithm that seeks the best
possible overlapping location. This determines from how wide a sample possible overlapping location. This determines from how wide a sample
"window" the algorithm can use to find an optimal mixing location when "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> <br>
The bigger this window setting is, the higher the possibility to find a 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 better mixing position becomes, but at the same time large values may
@ -348,7 +348,7 @@ computation burden</td>
</td> </td>
<td valign="top">Default value is relatively large, chosen to <td valign="top">Default value is relatively large, chosen to
suit with above parameters.</td> 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 <td valign="top">If you reduce the "sequence ms" setting, you
might wish to try a smaller value.</td> might wish to try a smaller value.</td>
<td valign="top">Increasing the parameter value increases <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 <p>The time-stretch routine has a 'quick' mode that substantially
speeds up the algorithm but may slightly compromise the sound quality. speeds up the algorithm but may slightly compromise the sound quality.
This mode is activated by calling SoundTouch::setSetting() 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> "1", i.e. </p>
<blockquote> <blockquote>
<p>setSetting(SETTING_USE_QUICKSEEK, 1);</p> <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>
<td valign="top">Name of the output sound file where the <td valign="top">Name of the output sound file where the
resulting sound is saved (in .WAV audio file format). This parameter 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 only calculating BPM rate with '-bpm' switch). Give "stdout" as
filename to use standard output pipe.</td> filename to use standard output pipe.</td>
</tr> </tr>
<tr> <tr>
<td valign="top"> <td valign="top">
<pre>&nbsp;[switches]</pre> <pre>[switches]</pre>
</td> </td>
<td valign="top">Are one or more control switches.</td> <td valign="top">Are one or more control switches.</td>
</tr> </tr>
@ -575,10 +575,11 @@ this corresponds to lowering the pitch by -0.318 semitones:</p>
<h3>5.1. SoundTouch library Change History </h3> <h3>5.1. SoundTouch library Change History </h3>
<p><b>2.0.1pre:</b></p> <p><b>2.0.1pre:</b></p>
<ul> <ul>
<li>Refactored C# interface example</li>
<li>Disable anti-alias filter when switch <li>Disable anti-alias filter when switch
SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias
filter cause slight click if the rate change crosses zero during filter cause slight click if the rate change crosses zero during
processing</li> processing</li>
</ul> </ul>
<p><b>2.0:</b></p> <p><b>2.0:</b></p>
<ul> <ul>
@ -670,13 +671,13 @@ sample batch sizes</li>
<ul> <ul>
<li> Added normalization to correlation calculation and improvement <li> Added normalization to correlation calculation and improvement
automatic seek/sequence parameter calculation to improve sound quality</li> automatic seek/sequence parameter calculation to improve sound quality</li>
<li> Bugfixes:&nbsp; <li> Bugfixes:
<ul> <ul>
<li> Fixed negative array indexing in quick seek algorithm</li> <li> Fixed negative array indexing in quick seek algorithm</li>
<li> FIR autoalias filter running too far in processing buffer</li> <li> FIR autoalias filter running too far in processing buffer</li>
<li> Check against zero sample count in rate transposing</li> <li> Check against zero sample count in rate transposing</li>
<li> Fix for x86-64 support: Removed pop/push instructions from <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> Check against empty buffers in FIFOSampleBuffer</li>
<li> Other minor fixes &amp; code cleanup</li> <li> Other minor fixes &amp; code cleanup</li>
</ul> </ul>
@ -692,7 +693,7 @@ negative side or vice versa</li>
<p><strong>1.4.1:</strong></p> <p><strong>1.4.1:</strong></p>
<ul> <ul>
<li> Fixed a buffer overflow bug in BPM detect algorithm routines if <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> </ul>
<p><strong>1.4.0:</strong></p> <p><strong>1.4.0:</strong></p>
<ul> <ul>
@ -775,7 +776,6 @@ accessing the FIFOSampleBuffer class from external files.</li>
<ul> <ul>
<li> Initial release</li> <li> Initial release</li>
</ul> </ul>
<p>&nbsp;</p>
<h3>5.2. SoundStretch application Change History </h3> <h3>5.2. SoundStretch application Change History </h3>
<p><b>1.9:</b></p> <p><b>1.9:</b></p>
<ul> <ul>
@ -862,7 +862,7 @@ submitted bugfixes:</p>
<li> Jason Garland</li> <li> Jason Garland</li>
<li> Takashi Iwai</li> <li> Takashi Iwai</li>
<li> Thomas Klausner</li> <li> Thomas Klausner</li>
<li> Lu Zhihe</li> <li> Lu Zhihe</li>
<li> Tony Mechelynck </li> <li> Tony Mechelynck </li>
<li> Mathias M&ouml;hl</li> <li> Mathias M&ouml;hl</li>
<li> Yuval Naveh</li> <li> Yuval Naveh</li>
@ -879,8 +879,9 @@ submitted bugfixes:</p>
<li> Albert Sirvent</li> <li> Albert Sirvent</li>
<li> Tyson Smith</li> <li> Tyson Smith</li>
<li> John Stumpo</li> <li> John Stumpo</li>
<li> Mario di Vece</li>
<li> Katja Vetter</li> <li> Katja Vetter</li>
<li> Wu Q.</li> <li> Wu Q.</li>
</ul> </ul>
<p>Moral greetings to all other contributors and users also!</p> <p>Moral greetings to all other contributors and users also!</p>
<hr> <hr>

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:csharp_example" xmlns:local="clr-namespace:csharp_example"
mc:Ignorable="d" 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"> <Grid Margin="0,0,0,-3">
<TextBlock HorizontalAlignment="Left" Margin="10,21,0,0" Text="Input audio file:" VerticalAlignment="Top"/> <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"/> <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 soundtouch;
using System; using System;
using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
@ -45,7 +46,7 @@ namespace csharp_example
string status; string status;
try try
{ {
status = String.Format("SoundTouch version: {0}", SoundTouch.GetVersionString()); status = String.Format("SoundTouch version: {0}", SoundTouch.Version);
} }
catch (Exception exp) 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) private void button_browse_Click(object sender, RoutedEventArgs e)
{ {
// Show file selection dialog
Microsoft.Win32.OpenFileDialog openDialog = new Microsoft.Win32.OpenFileDialog(); 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"; openDialog.Filter = "MP3 files (*.mp3)|*.mp3";
if (openDialog.ShowDialog() == true) if (openDialog.ShowDialog() == true)
{ {
if (processor.OpenMp3File(openDialog.FileName) == true) OpenFile(openDialog.FileName);
{
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);
}
} }
} }
@ -105,7 +123,7 @@ namespace csharp_example
private void button_play_Click(object sender, RoutedEventArgs e) private void button_play_Click(object sender, RoutedEventArgs e)
{ {
if (button_play.Content == "_Pause") if ((string)button_play.Content == "_Pause")
{ {
// Pause // Pause
if (processor.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()) 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) private bool parse_percentValue(TextBox box, out double value)
{ {
if (double.TryParse(box.Text, out value) == false) return false; if (double.TryParse(box.Text, out value) == false) return false;
@ -150,7 +174,7 @@ namespace csharp_example
double pitchValue; double pitchValue;
if (double.TryParse(textBox_pitch.Text, out 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; double tempoValue;
if (parse_percentValue(textBox_tempo, out 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; double rateValue;
if (parse_percentValue(textBox_rate, out 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(); 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; inputStr = input;
st = new SoundTouch(); st = new SoundTouch();
st.SetChannels((uint)input.WaveFormat.Channels); st.Channels = (uint)input.WaveFormat.Channels;
st.SetSampleRate((uint)input.WaveFormat.SampleRate); st.SampleRate = (uint)input.WaveFormat.SampleRate;
} }
/// <summary> /// <summary>
@ -123,7 +123,7 @@ namespace csharp_example
// Iterate until enough samples available for output: // Iterate until enough samples available for output:
// - read samples from input stream // - read samples from input stream
// - put samples to SoundStretch processor // - put samples to SoundStretch processor
while (st.NumSamples() < count) while (st.AvailableSampleCount < count)
{ {
int nbytes = inputStr.Read(bytebuffer, 0, bytebuffer.Length); int nbytes = inputStr.Read(bytebuffer, 0, bytebuffer.Length);
if (nbytes == 0) if (nbytes == 0)

View File

@ -6,6 +6,8 @@
/// Author e-mail : oparviai 'at' iki.fi /// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch /// SoundTouch WWW: http://www.surina.net/soundtouch
/// ///
/// The C# wrapper improved by Mario Di Vece
///
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// License : // License :
@ -34,37 +36,341 @@ using System.Runtime.InteropServices;
namespace soundtouch 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; private IntPtr handle;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="SoundTouch"/> class.
/// </summary>
public SoundTouch() public SoundTouch()
{ {
handle = soundtouch_createInstance(); handle = NativeMethods.CreateInstance();
} }
/// <summary>
/// Finalizes an instance of the <see cref="SoundTouch"/> class.
/// </summary>
~SoundTouch() ~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> /// <summary>
/// Get SoundTouch version string /// Get SoundTouch version string
/// </summary> /// </summary>
public static String GetVersionString() public static string Version
{ {
// convert "char *" data to c# string get
return Marshal.PtrToStringAnsi(soundtouch_getVersionString()); {
// 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> /// <summary>
/// Returns number of processed samples currently available in SoundTouch for immediate output. /// Returns number of processed samples currently available in SoundTouch for immediate output.
/// </summary> /// </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> /// <summary>
@ -78,274 +384,161 @@ namespace soundtouch
/// data for all channels</param> /// data for all channels</param>
public void PutSamples(float[] samples, uint numSamples) public void PutSamples(float[] samples, uint numSamples)
{ {
soundtouch_putSamples(handle, samples, numSamples); lock (SyncRoot) { NativeMethods.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);
} }
/// <summary> /// <summary>
/// Flushes the last samples from the processing pipeline to the output. /// int16 version of putSamples(): This accept int16 (short) sample data
/// 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
/// and internally converts it to float format before processing /// and internally converts it to float format before processing
/// </summary> /// </summary>
/// <param name="samples">Sample input buffer.</param> /// <param name="samples">Sample input buffer.</param>
/// <param name="numSamples">Number of sample frames in buffer. Notice /// <param name="numSamples">Number of sample frames in buffer. Notice
/// that in case of multi-channel sound a single /// that in case of multi-channel sound a single
/// sample frame contains data for all channels.</param> /// 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> /// <summary>
/// Changes a setting controlling the processing system behaviour. See the /// Receive processed samples from the processor.
/// 'SETTING_...' defines for available setting ID's.
/// </summary> /// </summary>
/// <param name="settingId">Setting ID number. see SETTING_... defines.</param> /// <param name="outBuffer">Buffer where to copy output samples</param>
/// <param name="value"New setting value></param> /// <param name="maxSamples">Max number of sample frames to receive</param>
/// <returns>nonzero if successful, otherwise zero</returns> /// <returns>The number of samples received</returns>
public int SetSetting(int settingId, int value) public uint ReceiveSamples(float[] outBuffer, uint maxSamples)
{ {
return soundtouch_setSetting(handle, settingId, value); lock (SyncRoot) { return NativeMethods.ReceiveSamples(handle, outBuffer, maxSamples); }
} }
/// <summary> /// <summary>
/// Reads a setting controlling the processing system behaviour. See the /// int16 version of receiveSamples(): This converts internal float samples
/// '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
/// into int16 (short) return data type /// into int16 (short) return data type
/// </summary> /// </summary>
/// <param name="outBuffer">Buffer where to copy output samples.</param> /// <param name="outBuffer">Buffer where to copy output samples.</param>
/// <param name="maxSamples">How many samples to receive at max.</param> /// <param name="maxSamples">How many samples to receive at max.</param>
/// <returns>Number of received sample frames</returns> /// <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> /// <summary>
/// Check if there aren't any samples available for outputting. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>
/// <returns>nonzero if there aren't any samples available for outputting</returns> /// <param name="alsoManaged"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
public int IsEmpty() 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> /// <summary>
/// Get SoundTouch library version Id /// Provides direct access to mapped DLL methods
/// </summary> /// </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)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_destroyInstance")]
private static extern IntPtr soundtouch_createInstance(); public static extern void DestroyInstance(IntPtr h);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_getVersionString")]
private static extern void soundtouch_destroyInstance(IntPtr h); public static extern IntPtr GetVersionString();
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setRate")]
private static extern IntPtr soundtouch_getVersionString(); public static extern void SetRate(IntPtr h, float newRate);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setTempo")]
private static extern void soundtouch_setRate(IntPtr h, float newRate); public static extern void SetTempo(IntPtr h, float newTempo);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setRateChange")]
private static extern void soundtouch_setTempo(IntPtr h, float newTempo); public static extern void SetRateChange(IntPtr h, float newRate);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setTempoChange")]
private static extern void soundtouch_setRateChange(IntPtr h, float newRate); public static extern void SetTempoChange(IntPtr h, float newTempo);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitch")]
private static extern void soundtouch_setTempoChange(IntPtr h, float newTempo); public static extern void SetPitch(IntPtr h, float newPitch);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitchOctaves")]
private static extern void soundtouch_setPitch(IntPtr h, float newPitch); public static extern void SetPitchOctaves(IntPtr h, float newPitch);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setPitchSemiTones")]
private static extern void soundtouch_setPitchOctaves(IntPtr h, float newPitch); public static extern void SetPitchSemiTones(IntPtr h, float newPitch);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setChannels")]
private static extern void soundtouch_setPitchSemiTones(IntPtr h, float newPitch); public static extern void SetChannels(IntPtr h, uint numChannels);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setSampleRate")]
private static extern void soundtouch_setChannels(IntPtr h, uint numChannels); public static extern void SetSampleRate(IntPtr h, uint srate);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_flush")]
private static extern void soundtouch_setSampleRate(IntPtr h, uint srate); public static extern void Flush(IntPtr h);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_putSamples")]
private static extern void soundtouch_flush(IntPtr h); public static extern void PutSamples(IntPtr h, float[] samples, uint numSamples);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_putSamples_i16")]
private static extern void soundtouch_putSamples(IntPtr h, float[] samples, uint numSamples); public static extern void PutSamples_i16(IntPtr h, short[] samples, uint numSamples);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_clear")]
private static extern void soundtouch_putSamples_i16(IntPtr h, short[] samples, uint numSamples); public static extern void Clear(IntPtr h);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_setSetting")]
private static extern void soundtouch_clear(IntPtr h); public static extern int SetSetting(IntPtr h, int settingId, int value);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_getSetting")]
private static extern int soundtouch_setSetting(IntPtr h, int settingId, int value); public static extern int GetSetting(IntPtr h, int settingId);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_numUnprocessedSamples")]
private static extern int soundtouch_getSetting(IntPtr h, int settingId); public static extern uint NumUnprocessedSamples(IntPtr h);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_receiveSamples")]
private static extern uint soundtouch_numUnprocessedSamples(IntPtr h); public static extern uint ReceiveSamples(IntPtr h, float[] outBuffer, uint maxSamples);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_receiveSamples_i16")]
private static extern uint soundtouch_receiveSamples(IntPtr h, float[] outBuffer, uint maxSamples); public static extern uint ReceiveSamples_i16(IntPtr h, short[] outBuffer, uint maxSamples);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_numSamples")]
private static extern uint soundtouch_receiveSamples_i16(IntPtr h, short[] outBuffer, uint maxSamples); public static extern uint NumSamples(IntPtr h);
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] [DllImport(SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "soundtouch_isEmpty")]
private static extern uint soundtouch_numSamples(IntPtr h); public static extern int IsEmpty(IntPtr h);
}
[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)] #endregion
private static extern int soundtouch_isEmpty(IntPtr h);
} }
} }