Skip to content

Commit

Permalink
Added SoundTouch to be able to adjust pitch and tempo with effecting …
Browse files Browse the repository at this point in the history
…the audio with side effects.
  • Loading branch information
ArizonaInkStudios committed May 26, 2018
1 parent 6a4cc67 commit 67f9d48
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 1 deletion.
8 changes: 7 additions & 1 deletion CSCore/CSCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@
<Compile Include="Streams\PeakMeter.cs" />
<Compile Include="Streams\SampleConverter\Pcm32BitToSample.cs" />
<Compile Include="Streams\SampleConverter\SampleToPcm32.cs" />
<Compile Include="Streams\SoundTouch.cs" />
<Compile Include="Streams\SoundTouchSource.cs" />
<Compile Include="Streams\SynchronizedWaveSource.cs" />
<Compile Include="Streams\WriteableBufferingSource.cs" />
<Compile Include="Streams\BufferSource.cs" />
Expand Down Expand Up @@ -619,9 +621,13 @@
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<Content Include="Libs\SoundTouch.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
<PostBuildEvent>xcopy /y /d "$(ProjectDir)Libs\*.dll" "$(TargetDir)"

@echo off &amp; setLocal EnableDELAYedeXpansion
chcp 65001

Expand Down
Binary file added CSCore/Libs/SoundTouch.dll
Binary file not shown.
122 changes: 122 additions & 0 deletions CSCore/Streams/SoundTouch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.Runtime.InteropServices;

namespace CSCore.Streams
{
public interface ISoundTouch : IDisposable
{
void Flush();
uint NumberOfSamples();
void PutSamples(float[] samples, uint numSamples);
uint ReceiveSamples(float[] outBuffer, uint maxSamples);
void SetChannels(uint numChannels);
void SetPitchSemiTones(float newPitch);
void SetSampleRate(uint srate);
void SetTempoChange(float newTempo);
}

public class SoundTouch : ISoundTouch
{
private bool disposed;
private readonly IntPtr _soundTouchHandle;

public SoundTouch()
{
_soundTouchHandle = soundtouch_createInstance();
}

public uint NumberOfSamples()
{
return soundtouch_numSamples(_soundTouchHandle);
}

public void PutSamples(float[] samples, uint numSamples)
{
soundtouch_putSamples(_soundTouchHandle, samples, numSamples);
}

public void SetChannels(uint numChannels)
{
soundtouch_setChannels(_soundTouchHandle, numChannels);
}

public void SetSampleRate(uint srate)
{
soundtouch_setSampleRate(_soundTouchHandle, srate);
}

public uint ReceiveSamples(float[] outBuffer, uint maxSamples)
{
return soundtouch_receiveSamples(_soundTouchHandle, outBuffer, maxSamples);
}

public void Flush()
{
soundtouch_flush(_soundTouchHandle);
}

public void SetTempoChange(float newTempo)
{
soundtouch_setTempoChange(_soundTouchHandle, newTempo);
}

public void SetPitchSemiTones(float newPitch)
{
soundtouch_setPitchSemiTones(_soundTouchHandle, newPitch);
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
if(disposed)
{
return;
}

if(disposing)
{
soundtouch_destroyInstance(_soundTouchHandle);
}

disposed = true;
}

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr soundtouch_createInstance();

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_destroyInstance(IntPtr h);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_setTempoChange(IntPtr h, float newTempo);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_setPitch(IntPtr h, float newPitch);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_setPitchSemiTones(IntPtr h, float newPitch);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_setChannels(IntPtr h, uint numChannels);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_setSampleRate(IntPtr h, uint srate);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_flush(IntPtr h);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void soundtouch_putSamples(IntPtr h, float[] samples, uint numSamples);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern uint soundtouch_receiveSamples(IntPtr h, float[] outBuffer, uint maxSamples);

[DllImport("SoundTouch.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern uint soundtouch_numSamples(IntPtr h);
}
}
93 changes: 93 additions & 0 deletions CSCore/Streams/SoundTouchSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;

namespace CSCore.Streams
{
public class SoundTouchSource : WaveAggregatorBase
{
private byte[] _bytebuffer = new byte[4096];
private float[] _floatbuffer = new float[1024];
private bool _endReached = false;
private readonly object lockObject;

private IWaveSource _waveSource;
private ISoundTouch _soundTouch;

public SoundTouchSource(IWaveSource waveSource)
: this(waveSource, new SoundTouch())
{
}

public SoundTouchSource(IWaveSource waveSource, ISoundTouch soundTouch)
: base(waveSource)
{
_waveSource = waveSource;

_soundTouch = soundTouch;
_soundTouch.SetChannels((uint)_waveSource.WaveFormat.Channels);
_soundTouch.SetSampleRate((uint)_waveSource.WaveFormat.SampleRate);

lockObject = new object();
}

public void SetPitch(float pitch)
{
if(pitch > 6.0f || pitch < -6.0f)
{
pitch = 0.0f;
}

_soundTouch.SetPitchSemiTones(pitch);
}

public void SetTempo(float tempo)
{
if(tempo > 52.0f || tempo < -52.0f)
{
tempo = 0.0f;
}

_soundTouch.SetTempoChange(tempo);
}

public override int Read(byte[] buffer, int offset, int count)
{
lock(lockObject)
{
try
{
while(_soundTouch.NumberOfSamples() < count)
{
var bytesRead = _waveSource.Read(_bytebuffer, offset, _bytebuffer.Length);
if(bytesRead == 0)
{
if(_endReached == false)
{
_endReached = true;
_soundTouch.Flush();
}

break;
}

Buffer.BlockCopy(_bytebuffer, 0, _floatbuffer, 0, bytesRead);
_soundTouch.PutSamples(_floatbuffer, (uint)(bytesRead / 8));
}

if(_floatbuffer.Length < count / 4)
{
_floatbuffer = new float[count / 4];
}

var numberOfSamples = (int)_soundTouch.ReceiveSamples(_floatbuffer, (uint)(count / 8));
Buffer.BlockCopy(_floatbuffer, 0, buffer, offset, numberOfSamples * 8);

return numberOfSamples * 8;
}
catch
{
return 0;
}
}
}
}
}

0 comments on commit 67f9d48

Please sign in to comment.