From ec9ba968f5483d855bdbd65711d287349ba9e792 Mon Sep 17 00:00:00 2001
From: oparviai
Date: Fri, 10 Nov 2017 16:38:36 +0000
Subject: [PATCH] Added BPMDetect functions to SoundTouchDll API
---
README.html | 2 +
include/SoundTouch.h | 4 +-
source/SoundTouchDLL/SoundTouchDLL.cpp | 102 +++++++++++++++++-
source/SoundTouchDLL/SoundTouchDLL.h | 30 ++++++
source/csharp-example/SoundTouch.cs | 141 ++++++++++++++++++++++++-
5 files changed, 274 insertions(+), 5 deletions(-)
diff --git a/README.html b/README.html
index 4e6f91b..28d3a89 100644
--- a/README.html
+++ b/README.html
@@ -580,6 +580,8 @@ this corresponds to lowering the pitch by -0.318 semitones:
SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER defined because anti-alias
filter cause slight click if the rate change crosses zero during
processing
+ Added script for building SoundTouchDll dynamic-link-library for GNU platforms
+ Added BPM functions to SoundTouchDll API
2.0:
diff --git a/include/SoundTouch.h b/include/SoundTouch.h
index 51c3389..6cb8d15 100644
--- a/include/SoundTouch.h
+++ b/include/SoundTouch.h
@@ -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:
diff --git a/source/SoundTouchDLL/SoundTouchDLL.cpp b/source/SoundTouchDLL/SoundTouchDLL.cpp
index b24485d..3591f29 100644
--- a/source/SoundTouchDLL/SoundTouchDLL.cpp
+++ b/source/SoundTouchDLL/SoundTouchDLL.cpp
@@ -60,6 +60,7 @@
#include
#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();
+}
diff --git a/source/SoundTouchDLL/SoundTouchDLL.h b/source/SoundTouchDLL/SoundTouchDLL.h
index 47533be..5472d48 100644
--- a/source/SoundTouchDLL/SoundTouchDLL.h
+++ b/source/SoundTouchDLL/SoundTouchDLL.h
@@ -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_
diff --git a/source/csharp-example/SoundTouch.cs b/source/csharp-example/SoundTouch.cs
index 9682615..d68df6a 100644
--- a/source/csharp-example/SoundTouch.cs
+++ b/source/csharp-example/SoundTouch.cs
@@ -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
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BPMDetect(int numChannels, int sampleRate)
+ {
+ handle = NativeMethods.BpmCreateInstance(numChannels, sampleRate);
+ }
+
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~BPMDetect()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(false);
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Returns the analysed BPM rate.
+ ///
+ public float Bpm
+ {
+ get { lock (SyncRoot) { return NativeMethods.BpmGet(handle); } }
+ }
+
+ #endregion
+
+ #region Sample Stream Methods
+
+ ///
+ /// Feed 'numSamples' sample into the BPM detector
+ ///
+ /// Sample buffer to input
+ /// Number of sample frames in buffer. Notice
+ /// that in case of multi-channel sound a single sample frame contains
+ /// data for all channels
+ public void PutSamples(float[] samples, uint numSamples)
+ {
+ lock (SyncRoot) { NativeMethods.BpmPutSamples(handle, samples, numSamples); }
+ }
+
+ ///
+ /// int16 version of putSamples(): This accept int16 (short) sample data
+ /// and internally converts it to float format before processing
+ ///
+ /// Sample input buffer.
+ /// Number of sample frames in buffer. Notice
+ /// that in case of multi-channel sound a single
+ /// sample frame contains data for all channels.
+ public void PutSamplesI16(short[] samples, uint numSamples)
+ {
+ lock (SyncRoot) { NativeMethods.BpmPutSamples_i16(handle, samples, numSamples); }
+ }
+
+ #endregion
+
+ #region IDisposable Support
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Releases unmanaged and - optionally - managed resources.
+ ///
+ /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ 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
+
+ ///
+ /// Provides direct access to mapped DLL methods
+ ///
+ 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
+ }
+
}
\ No newline at end of file