forked from filoe/cscore
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added SoundTouch to be able to adjust pitch and tempo with effecting …
…the audio with side effects.
- Loading branch information
1 parent
6a4cc67
commit 67f9d48
Showing
4 changed files
with
222 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} | ||
} | ||
} |