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

Added BPMDetect functions to SoundTouchDll API

This commit is contained in:
oparviai 2017-11-10 16:38:36 +00:00
parent 407f516e0d
commit ec9ba968f5
5 changed files with 274 additions and 5 deletions

View File

@ -580,6 +580,8 @@ this corresponds to lowering the pitch by -0.318 semitones:</p>
SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias
filter cause slight click if the rate change crosses zero during
processing</li>
<li>Added script for building SoundTouchDll dynamic-link-library for GNU platforms</li>
<li>Added BPM functions to SoundTouchDll API</li>
</ul>
<p><b>2.0:</b></p>
<ul>

View File

@ -79,10 +79,10 @@ namespace soundtouch
{
/// Soundtouch library version string
#define SOUNDTOUCH_VERSION "2.0.0"
#define SOUNDTOUCH_VERSION "2.0.1pre"
/// SoundTouch library version id
#define SOUNDTOUCH_VERSION_ID (20000)
#define SOUNDTOUCH_VERSION_ID (20001)
//
// Available setting IDs for the 'setSetting' & 'get_setting' functions:

View File

@ -60,6 +60,7 @@
#include <string.h>
#include "SoundTouchDLL.h"
#include "SoundTouch.h"
#include "BPMDetect.h"
using namespace soundtouch;
@ -75,7 +76,15 @@ typedef struct
SoundTouch *pst;
} STHANDLE;
#define STMAGIC 0x1770C001
typedef struct
{
DWORD dwMagic;
BPMDetect *pbpm;
uint numChannels;
} BPMHANDLE;
#define STMAGIC 0x1770C001
#define BPMMAGIC 0x1771C10a
SOUNDTOUCHDLL_API HANDLE __cdecl soundtouch_createInstance()
{
@ -359,6 +368,7 @@ SOUNDTOUCHDLL_API uint __cdecl soundtouch_receiveSamples(HANDLE h,
}
}
/// int16 version of soundtouch_receiveSamples(): This converts internal float samples
/// into int16 (short) return data type
SOUNDTOUCHDLL_API uint __cdecl soundtouch_receiveSamples_i16(HANDLE h,
@ -429,3 +439,93 @@ SOUNDTOUCHDLL_API int __cdecl soundtouch_isEmpty(HANDLE h)
return sth->pst->isEmpty();
}
SOUNDTOUCHDLL_API HANDLE __cdecl bpm_createInstance(int numChannels, int sampleRate)
{
BPMHANDLE *tmp = new BPMHANDLE;
if (tmp)
{
tmp->dwMagic = BPMMAGIC;
tmp->pbpm = new BPMDetect(numChannels, sampleRate);
if (tmp->pbpm == NULL)
{
delete tmp;
tmp = NULL;
}
}
return (HANDLE)tmp;
}
SOUNDTOUCHDLL_API void __cdecl bpm_destroyInstance(HANDLE h)
{
BPMHANDLE *sth = (BPMHANDLE*)h;
if (sth->dwMagic != BPMMAGIC) return;
sth->dwMagic = 0;
if (sth->pbpm) delete sth->pbpm;
sth->pbpm = NULL;
delete sth;
}
/// Feed 'numSamples' sample frames from 'samples' into the BPM detection handler
SOUNDTOUCHDLL_API void __cdecl bpm_putSamples(HANDLE h,
const float *samples,
unsigned int numSamples)
{
BPMHANDLE *bpmh = (BPMHANDLE*)h;
if (bpmh->dwMagic != BPMMAGIC) return;
bpmh->pbpm->inputSamples(samples, numSamples);
}
/// Feed 'numSamples' sample frames from 'samples' into the BPM detection handler.
/// 16bit int sample format verson.
SOUNDTOUCHDLL_API void __cdecl bpm_putSamples_i16(HANDLE h,
const short *samples,
unsigned int numSamples)
{
BPMHANDLE *bpmh = (BPMHANDLE*)h;
if (bpmh->dwMagic != BPMMAGIC) return;
uint numChannels = bpmh->numChannels;
// iterate until all samples converted & put to SoundTouch object
while (numSamples > 0)
{
float convert[8192]; // allocate temporary conversion buffer from stack
// how many multichannel samples fit into 'convert' buffer:
uint convSamples = 8192 / numChannels;
// convert max 'nround' values at a time to guarantee that these fit in the 'convert' buffer
uint n = (numSamples > convSamples) ? convSamples : numSamples;
for (uint i = 0; i < n * numChannels; i++)
{
convert[i] = samples[i];
}
// put the converted samples into SoundTouch
bpmh->pbpm->inputSamples(convert, n);
numSamples -= n;
samples += n * numChannels;
}
}
/// Analyzes the results and returns the BPM rate. Use this function to read result
/// after whole song data has been input to the class by consecutive calls of
/// 'inputSamples' function.
///
/// \return Beats-per-minute rate, or zero if detection failed.
SOUNDTOUCHDLL_API float __cdecl bpm_getBpm(HANDLE h)
{
BPMHANDLE *bpmh = (BPMHANDLE*)h;
if (bpmh->dwMagic != BPMMAGIC) return 0;
return bpmh->pbpm->getBpm();
}

View File

@ -199,5 +199,35 @@ SOUNDTOUCHDLL_API unsigned int __cdecl soundtouch_numSamples(HANDLE h);
/// Returns nonzero if there aren't any samples available for outputting.
SOUNDTOUCHDLL_API int __cdecl soundtouch_isEmpty(HANDLE h);
/// Create a new instance of BPM detector
SOUNDTOUCHDLL_API HANDLE __cdecl bpm_createInstance(int numChannels, int sampleRate);
/// Destroys a BPM detector instance.
SOUNDTOUCHDLL_API void __cdecl bpm_destroyInstance(HANDLE h);
/// Feed 'numSamples' sample frames from 'samples' into the BPM detector.
SOUNDTOUCHDLL_API void __cdecl bpm_putSamples(HANDLE h,
const float *samples, ///< Pointer to sample buffer.
unsigned int numSamples ///< Number of samples in buffer. Notice
///< that in case of stereo-sound a single sample
///< contains data for both channels.
);
/// Feed 'numSamples' sample frames from 'samples' into the BPM detector.
/// 16bit int sample format verson.
SOUNDTOUCHDLL_API void __cdecl bpm_putSamples_i16(HANDLE h,
const short *samples, ///< Pointer to sample buffer.
unsigned int numSamples ///< Number of samples in buffer. Notice
///< that in case of stereo-sound a single sample
///< contains data for both channels.
);
/// Analyzes the results and returns the BPM rate. Use this function to read result
/// after whole song data has been input to the class by consecutive calls of
/// 'inputSamples' function.
///
/// \return Beats-per-minute rate, or zero if detection failed.
SOUNDTOUCHDLL_API float __cdecl bpm_getBpm(HANDLE h);
#endif // _SoundTouchDLL_h_

View File

@ -38,9 +38,12 @@ namespace soundtouch
{
public sealed class SoundTouch : IDisposable
{
#region Private Members
#region Internal Members
internal const string SoundTouchLibrary = "SoundTouch.dll";
#endregion
#region Private Members // hahaha what a curious region
private const string SoundTouchLibrary = "SoundTouch.dll";
private readonly object SyncRoot = new object();
private bool IsDisposed = false;
private IntPtr handle;
@ -541,4 +544,138 @@ namespace soundtouch
#endregion
}
public sealed class BPMDetect : IDisposable
{
#region Private Members
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="BPMDetect"/> class.
/// </summary>
public BPMDetect(int numChannels, int sampleRate)
{
handle = NativeMethods.BpmCreateInstance(numChannels, sampleRate);
}
/// <summary>
/// Finalizes an instance of the <see cref="BPMDetect"/> class.
/// </summary>
~BPMDetect()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(false);
}
#endregion
#region Properties
/// <summary>
/// Returns the analysed BPM rate.
/// </summary>
public float Bpm
{
get { lock (SyncRoot) { return NativeMethods.BpmGet(handle); } }
}
#endregion
#region Sample Stream Methods
/// <summary>
/// Feed 'numSamples' sample into the BPM detector
/// </summary>
/// <param name="samples">Sample buffer to input</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(float[] samples, uint numSamples)
{
lock (SyncRoot) { NativeMethods.BpmPutSamples(handle, samples, numSamples); }
}
/// <summary>
/// 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 PutSamplesI16(short[] samples, uint numSamples)
{
lock (SyncRoot) { NativeMethods.BpmPutSamples_i16(handle, samples, numSamples); }
}
#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>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <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)
{
if (!IsDisposed)
{
if (alsoManaged)
{
// NOTE: Placeholder, dispose managed state (managed objects).
// At this point, nothing managed to dispose
}
NativeMethods.BpmDestroyInstance(handle);
handle = IntPtr.Zero;
IsDisposed = true;
}
}
#endregion
#region Native Methods
/// <summary>
/// Provides direct access to mapped DLL methods
/// </summary>
private static class NativeMethods
{
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_createInstance")]
public static extern IntPtr BpmCreateInstance(int numChannels, int sampleRate);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_destroyInstance")]
public static extern void BpmDestroyInstance(IntPtr h);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_putSamples")]
public static extern void BpmPutSamples(IntPtr h, float[] samples, uint numSamples);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_putSamples_i16")]
public static extern void BpmPutSamples_i16(IntPtr h, short[] samples, uint numSamples);
[DllImport(SoundTouch.SoundTouchLibrary, CallingConvention = CallingConvention.Cdecl, EntryPoint = "bpm_getBpm")]
public static extern float BpmGet(IntPtr h);
}
#endregion
}
}