packages feed

dywapitchtrack (empty) → 0.1.0.0

raw patch · 7 files changed

+732/−0 lines, 7 filesdep +basedep +bytestringdep +transformerssetup-changed

Dependencies added: base, bytestring, transformers

Files

+ LICENSE view
@@ -0,0 +1,21 @@+The MIT License (MIT)++Copyright (c) 2015 Lorenzo Tabacchini++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ cbits/dywapitchtrack.c view
@@ -0,0 +1,448 @@+/* dywapitchtrack.c+ + Dynamic Wavelet Algorithm Pitch Tracking library+ Released under the MIT open source licence+  + Copyright (c) 2010 Antoine Schmitt+ + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+*/++#include "dywapitchtrack.h"+#include <math.h>+#include <stdlib.h>+#include <string.h> // for memset+++//**********************+//       Utils+//**********************++#ifndef max+#define max(x, y) ((x) > (y)) ? (x) : (y)+#endif+#ifndef min+#define min(x, y) ((x) < (y)) ? (x) : (y)+#endif++// returns 1 if power of 2+int _power2p(int value) {+	if (value == 0) return 1;+	if (value == 2) return 1;+	if (value & 0x1) return 0;+	return (_power2p(value >> 1));+}++// count number of bits+int _bitcount(int value) {+	if (value == 0) return 0;+	if (value == 1) return 1;+	if (value == 2) return 2;+	return _bitcount(value >> 1) + 1;+}++// closest power of 2 above or equal+int _ceil_power2(int value) {+	if (_power2p(value)) return value;+	+	if (value == 1) return 2;+	int j, i = _bitcount(value);+	int res = 1;+	for (j = 0; j < i; j++) res <<= 1;+	return res;+}++// closest power of 2 below or equal+int _floor_power2(int value) {+	if (_power2p(value)) return value;+	return _ceil_power2(value)/2;+}++// abs value+int _iabs(int x) {+	if (x >= 0) return x;+	return -x;+}++// 2 power+int _2power(int i) {+	int res = 1, j;+	for (j = 0; j < i; j++) res <<= 1;+	return res;+}++//******************************+// the Wavelet algorithm itself+//******************************++int dywapitch_neededsamplecount(int minFreq) {+	int nbSam = 3*44100/minFreq; // 1017. for 130 Hz+	nbSam = _ceil_power2(nbSam); // 1024+	return nbSam;+}++typedef struct _minmax {+	int index;+	struct _minmax *next;+} minmax;++double _dywapitch_computeWaveletPitch(double * samples, int startsample, int samplecount) {+	double pitchF = 0.0;+	+	int i, j;+	double si, si1;+	+	// must be a power of 2+	samplecount = _floor_power2(samplecount);+	+	double *sam = (double *)malloc(sizeof(double)*samplecount);+	memcpy(sam, samples + startsample, sizeof(double)*samplecount);+	int curSamNb = samplecount;+	+	int *distances = (int *)malloc(sizeof(int)*samplecount);+	int *mins = (int *)malloc(sizeof(int)*samplecount);+	int *maxs = (int *)malloc(sizeof(int)*samplecount);+	int nbMins, nbMaxs;+	+	// algorithm parameters+	int maxFLWTlevels = 6;+	double maxF = 3000.;+	int differenceLevelsN = 3;+	double maximaThresholdRatio = 0.75;+	+	double ampltitudeThreshold;  +	double theDC = 0.0;+	+	{ // compute ampltitudeThreshold and theDC+		//first compute the DC and maxAMplitude+		double maxValue = 0.0;+		double minValue = 0.0;+		for (i = 0; i < samplecount;i++) {+			si = sam[i];+			theDC = theDC + si;+			if (si > maxValue) maxValue = si;+			if (si < minValue) minValue = si;+		}+		theDC = theDC/samplecount;+		maxValue = maxValue - theDC;+		minValue = minValue - theDC;+		double amplitudeMax = (maxValue > -minValue ? maxValue : -minValue);+		+		ampltitudeThreshold = amplitudeMax*maximaThresholdRatio;+		//asLog("dywapitch theDC=%f ampltitudeThreshold=%f\n", theDC, ampltitudeThreshold);+		+	}+	+	// levels, start without downsampling..+	int curLevel = 0;+	double curModeDistance = -1.;+	int delta;+	+	while(1) {+		+		// delta+		delta = 44100./(_2power(curLevel)*maxF);+		//("dywapitch doing level=%ld delta=%ld\n", curLevel, delta);+		+		if (curSamNb < 2) goto cleanup;+		+		// compute the first maximums and minumums after zero-crossing+		// store if greater than the min threshold+		// and if at a greater distance than delta+		double dv, previousDV = -1000;+		nbMins = nbMaxs = 0;   +		int lastMinIndex = -1000000;+		int lastmaxIndex = -1000000;+		int findMax = 0;+		int findMin = 0;+		for (i = 2; i < curSamNb; i++) {+			si = sam[i] - theDC;+			si1 = sam[i-1] - theDC;+			+			if (si1 <= 0 && si > 0) findMax = 1;+			if (si1 >= 0 && si < 0) findMin = 1;+			+			// min or max ?+			dv = si - si1;+			+			if (previousDV > -1000) {+				+				if (findMin && previousDV < 0 && dv >= 0) { +					// minimum+					if (fabs(si) >= ampltitudeThreshold) {+						if (i > lastMinIndex + delta) {+							mins[nbMins++] = i;+							lastMinIndex = i;+							findMin = 0;+							//if DEBUGG then put "min ok"&&si+							//+						} else {+							//if DEBUGG then put "min too close to previous"&&(i - lastMinIndex)+							//+						}+					} else {+						// if DEBUGG then put "min "&abs(si)&" < thresh = "&ampltitudeThreshold+						//--+					}+				}+				+				if (findMax && previousDV > 0 && dv <= 0) {+					// maximum+					if (fabs(si) >= ampltitudeThreshold) {+						if (i > lastmaxIndex + delta) {+							maxs[nbMaxs++] = i;+							lastmaxIndex = i;+							findMax = 0;+						} else {+							//if DEBUGG then put "max too close to previous"&&(i - lastmaxIndex)+							//--+						}+					} else {+						//if DEBUGG then put "max "&abs(si)&" < thresh = "&ampltitudeThreshold+						//--+					}+				}+			}+			+			previousDV = dv;+		}+		+		if (nbMins == 0 && nbMaxs == 0) {+			// no best distance !+			//asLog("dywapitch no mins nor maxs, exiting\n");+			+			// if DEBUGG then put "no mins nor maxs, exiting"+			goto cleanup;+		}+		//if DEBUGG then put count(maxs)&&"maxs &"&&count(mins)&&"mins"+		+		// maxs = [5, 20, 100,...]+		// compute distances+		int d;+		memset(distances, 0, samplecount*sizeof(int));+		for (i = 0 ; i < nbMins ; i++) {+			for (j = 1; j < differenceLevelsN; j++) {+				if (i+j < nbMins) {+					d = _iabs(mins[i] - mins[i+j]);+					//asLog("dywapitch i=%ld j=%ld d=%ld\n", i, j, d);+					distances[d] = distances[d] + 1;+				}+			}+		}+		for (i = 0 ; i < nbMaxs ; i++) {+			for (j = 1; j < differenceLevelsN; j++) {+				if (i+j < nbMaxs) {+					d = _iabs(maxs[i] - maxs[i+j]);+					//asLog("dywapitch i=%ld j=%ld d=%ld\n", i, j, d);+					distances[d] = distances[d] + 1;+				}+			}+		}+		+		// find best summed distance+		int bestDistance = -1;+		int bestValue = -1;+		for (i = 0; i< curSamNb; i++) {+			int summed = 0;+			for (j = -delta ; j <= delta ; j++) {+				if (i+j >=0 && i+j < curSamNb)+					summed += distances[i+j];+			}+			//asLog("dywapitch i=%ld summed=%ld bestDistance=%ld\n", i, summed, bestDistance);+			if (summed == bestValue) {+				if (i == 2*bestDistance)+					bestDistance = i;+				+			} else if (summed > bestValue) {+				bestValue = summed;+				bestDistance = i;+			}+		}+		//asLog("dywapitch bestDistance=%ld\n", bestDistance);+		+		// averaging+		double distAvg = 0.0;+		double nbDists = 0;+		for (j = -delta ; j <= delta ; j++) {+			if (bestDistance+j >=0 && bestDistance+j < samplecount) {+				int nbDist = distances[bestDistance+j];+				if (nbDist > 0) {+					nbDists += nbDist;+					distAvg += (bestDistance+j)*nbDist;+				}+			}+		}+		// this is our mode distance !+		distAvg /= nbDists;+		//asLog("dywapitch distAvg=%f\n", distAvg);+		+		// continue the levels ?+		if (curModeDistance > -1.) {+			double similarity = fabs(distAvg*2 - curModeDistance);+			if (similarity <= 2*delta) {+				//if DEBUGG then put "similarity="&similarity&&"delta="&delta&&"ok"+ 				//asLog("dywapitch similarity=%f OK !\n", similarity);+				// two consecutive similar mode distances : ok !+				pitchF = 44100./(_2power(curLevel-1)*curModeDistance);+				goto cleanup;+			}+			//if DEBUGG then put "similarity="&similarity&&"delta="&delta&&"not"+		}+		+		// not similar, continue next level+		curModeDistance = distAvg;+		+		curLevel = curLevel + 1;+		if (curLevel >= maxFLWTlevels) {+			// put "max levels reached, exiting"+ 			//asLog("dywapitch max levels reached, exiting\n");+			goto cleanup;+		}+		+		// downsample+		if (curSamNb < 2) {+ 			//asLog("dywapitch not enough samples, exiting\n");+			goto cleanup;+		}+		for (i = 0; i < curSamNb/2; i++) {+			sam[i] = (sam[2*i] + sam[2*i + 1])/2.;+		}+		curSamNb /= 2;+	}+	+	///+cleanup:+	free(distances);+	free(mins);+	free(maxs);+	free(sam);+	+	return pitchF;+}++// ***********************************+// the dynamic postprocess+// ***********************************++/***+It states: + - a pitch cannot change much all of a sudden (20%) (impossible humanly,+ so if such a situation happens, consider that it is a mistake and drop it. + - a pitch cannot double or be divided by 2 all of a sudden : it is an+ algorithm side-effect : divide it or double it by 2. + - a lonely voiced pitch cannot happen, nor can a sudden drop in the middle+ of a voiced segment. Smooth the plot. +***/++double _dywapitch_dynamicprocess(dywapitchtracker *pitchtracker, double pitch) {+	+	// equivalence+	if (pitch == 0.0) pitch = -1.0;+	+	//+	double estimatedPitch = -1;+	double acceptedError = 0.2f;+	int maxConfidence = 5;+	+	if (pitch != -1) {+		// I have a pitch here+		+		if (pitchtracker->_prevPitch == -1) {+			// no previous+			estimatedPitch = pitch;+			pitchtracker->_prevPitch = pitch;+			pitchtracker->_pitchConfidence = 1;+			+		} else if (abs(pitchtracker->_prevPitch - pitch)/pitch < acceptedError) {+			// similar : remember and increment pitch+			pitchtracker->_prevPitch = pitch;+			estimatedPitch = pitch;+			pitchtracker->_pitchConfidence = min(maxConfidence, pitchtracker->_pitchConfidence + 1); // maximum 3+			+		} else if ((pitchtracker->_pitchConfidence >= maxConfidence-2) && abs(pitchtracker->_prevPitch - 2.*pitch)/(2.*pitch) < acceptedError) {+			// close to half the last pitch, which is trusted+			estimatedPitch = 2.*pitch;+			pitchtracker->_prevPitch = estimatedPitch;+			+		} else if ((pitchtracker->_pitchConfidence >= maxConfidence-2) && abs(pitchtracker->_prevPitch - 0.5*pitch)/(0.5*pitch) < acceptedError) {+			// close to twice the last pitch, which is trusted+			estimatedPitch = 0.5*pitch;+			pitchtracker->_prevPitch = estimatedPitch;+			+		} else {+			// nothing like this : very different value+			if (pitchtracker->_pitchConfidence >= 1) {+				// previous trusted : keep previous+				estimatedPitch = pitchtracker->_prevPitch;+				pitchtracker->_pitchConfidence = max(0, pitchtracker->_pitchConfidence - 1);+			} else {+				// previous not trusted : take current+				estimatedPitch = pitch;+				pitchtracker->_prevPitch = pitch;+				pitchtracker->_pitchConfidence = 1;+			}+		}+		+	} else {+		// no pitch now+		if (pitchtracker->_prevPitch != -1) {+			// was pitch before+			if (pitchtracker->_pitchConfidence >= 1) {+				// continue previous+				estimatedPitch = pitchtracker->_prevPitch;+				pitchtracker->_pitchConfidence = max(0, pitchtracker->_pitchConfidence - 1);+			} else {+				pitchtracker->_prevPitch = -1;+				estimatedPitch = -1.;+				pitchtracker->_pitchConfidence = 0;+			}+		}+	}+	+	// put "_pitchConfidence="&pitchtracker->_pitchConfidence+	if (pitchtracker->_pitchConfidence >= 1) {+		// ok+		pitch = estimatedPitch;+	} else {+		pitch = -1;+	}+	+	// equivalence+	if (pitch == -1) pitch = 0.0;+	+	return pitch;+}+++// ************************************+// the API main entry points+// ************************************++void dywapitch_inittracking(dywapitchtracker *pitchtracker) {+	pitchtracker->_prevPitch = -1.;+	pitchtracker->_pitchConfidence = -1;+}++double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount) {+	double raw_pitch = _dywapitch_computeWaveletPitch(samples, startsample, samplecount);+	return _dywapitch_dynamicprocess(pitchtracker, raw_pitch);+}+++
+ dywapitchtrack.cabal view
@@ -0,0 +1,48 @@+name: dywapitchtrack+version: 0.1.0.0+cabal-version: >=1.10+build-type: Simple+license: MIT+license-file: LICENSE+copyright: (c) 2015 Lorenzo Tabacchini+maintainer: lortabac@gmx.com+bug-reports: https://github.com/lortabac/pitchtrack/issues+synopsis: Bindings to the dywapitchtrack pitch tracking library+description:+    Bindings to the+    <http://www.schmittmachine.com/dywapitchtrack.html dywapitchtrack>+    pitch tracking library by Antoine Schmitt.+    .+    The algorithm is described in a paper by Eric Larson and Ross Maddox:+    <http://courses.physics.illinois.edu/phys406/NSF_REU_Reports/2005_reu/Real-Time_Time-Domain_Pitch_Tracking_Using_Wavelets.pdf Real-Time Time-Domain Pitch Tracking Using Wavelets>.+    .+    For a higher-level interface, use the 'pitchtrack' package.+category: Sound+author: Lorenzo Tabacchini+tested-with: GHC >=7.10+extra-source-files:+    cbits/dywapitchtrack.c+    include/dywapitchtrack.h++source-repository head+    type: git+    location: https://github.com/lortabac/pitchtrack.git++library+    exposed-modules:+        DywaPitchTrack+    build-depends:+        base ==4.8.*,+        bytestring >=0.10.6.0 && <0.11,+        transformers >=0.4.2.0 && <0.5+    c-sources:+        cbits/dywapitchtrack.c+    default-language: Haskell2010+    includes:+        dywapitchtrack.h+    include-dirs: include+    hs-source-dirs: src+    other-modules:+        DywaPitchTrack.Internal.Bindings+    ghc-options: -Wall+
+ include/dywapitchtrack.h view
@@ -0,0 +1,110 @@+/* dywapitchtrack.h+ + Dynamic Wavelet Algorithm Pitch Tracking library+ Released under the MIT open source licence+ + Copyright (c) 2010 Antoine Schmitt+  + Permission is hereby granted, free of charge, to any person obtaining a copy+ of this software and associated documentation files (the "Software"), to deal+ in the Software without restriction, including without limitation the rights+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+ copies of the Software, and to permit persons to whom the Software is+ furnished to do so, subject to the following conditions:+ + The above copyright notice and this permission notice shall be included in+ all copies or substantial portions of the Software.+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+ THE SOFTWARE.+*/++/* Documentation+ + The dywapitchtrack library computes the pitch of an audio stream in real time.+ + The pitch is the main frequency of the waveform (the 'note' being played or sung).+ It is expressed as a float in Hz.+ + Unlike the human ear, pitch detection is difficult to achieve for computers. Many+ algorithms have been designed and experimented, but there is no 'best' algorithm.+ They all depend on the context and the tradeoffs acceptable in terms of speed and+ latency. The context includes the quality and 'cleanness' of the audio : obviously+ polyphonic sounds (multiple instruments playing different notes at the same time)+ are extremely difficult to track, percussive or noisy audio has no pitch, most+ real-life audio have some noisy moments, some instruments have a lot of harmonics,+ etc...+ + The dywapitchtrack is based on a custom-tailored algorithm which is of very high quality:+ both very accurate (precision < 0.05 semitones), very low latency (< 23 ms) and+ very low error rate. It has been  thoroughly tested on human voice.+ + It can best be described as a dynamic wavelet algorithm (dywa):+ + The heart of the algorithm is a very powerful wavelet algorithm, described in a paper+ by Eric Larson and Ross Maddox : "Real-Time Time-Domain Pitch Tracking Using Wavelets"+ http://online.physics.uiuc.edu/courses/phys498pom/NSF_REU_Reports/2005_reu/Real-Time_Time-Domain_Pitch_Tracking_Using_Wavelets.pdf+ + This central algorithm has been improved by adding dynamic tracking, to reduce the+ common problems of frequency-halving and voiced/unvoiced errors. This dynamic tracking+ explains the need for a tracking structure (dywapitchtracker). The dynamic tracking assumes+ that the main function dywapitch_computepitch is called repeatedly, as it follows the pitch+ over time and makes assumptions about human voice capabilities and reallife conditions+ (as documented inside the code).+ + Note : The algorithm currently assumes a 44100Hz audio sampling rate. If you use a different+ samplerate, you can just multiply the resulting pitch by the ratio between your samplerate and 44100.+*/++/* Usage++ // Allocate your audio buffers and start the audio stream.+ // Allocate a 'dywapitchtracker' structure.+ // Start the pitch tracking by calling 'dywapitch_inittracking'.+ dywapitchtracker pitchtracker;+ dywapitch_inittracking(&pitchtracker);+ + // For each available audio buffer, call 'dywapitch_computepitch'+ double thepitch = dywapitch_computepitch(&pitchtracker, samples, start, count);+ +*/++#ifndef dywapitchtrack__H+#define dywapitchtrack__H++#ifdef __cplusplus+extern "C" {+#endif++// structure to hold tracking data+typedef struct _dywapitchtracker {+	double	_prevPitch;+	int		_pitchConfidence;+} dywapitchtracker;++// returns the number of samples needed to compute pitch for fequencies equal and above the given minFreq (in Hz)+// useful to allocate large enough audio buffer +// ex : for frequencies above 130Hz, you need 1024 samples (assuming a 44100 Hz samplerate)+int dywapitch_neededsamplecount(int minFreq);++// call before computing any pitch, passing an allocated dywapitchtracker structure+void dywapitch_inittracking(dywapitchtracker *pitchtracker);++// computes the pitch. Pass the inited dywapitchtracker structure+// samples : a pointer to the sample buffer+// startsample : the index of teh first sample to use in teh sample buffer+// samplecount : the number of samples to use to compte the pitch+// return 0.0 if no pitch was found (sound too low, noise, etc..)+double dywapitch_computepitch(dywapitchtracker *pitchtracker, double * samples, int startsample, int samplecount);++#ifdef __cplusplus+} // extern "C"+#endif++#endif+
+ src/DywaPitchTrack.hs view
@@ -0,0 +1,62 @@+{- |+Note that because all parameters are hard-coded into the C library,+you are limited to the following audio configuration:++  * raw (headerless) format++  * a sampling rate of 44100Hz,++  * a sample size of @sizeof(double)@++  * floating-point encoding++  * one channel (mono)+-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module DywaPitchTrack (+    PitchTrack+  , runPitchTrack+  , computePitch+  , neededSampleNum+  , sampleSize+  ) where++import           DywaPitchTrack.Internal.Bindings++import           Control.Monad.IO.Class+import           Control.Monad.Trans.Reader+import           Data.ByteString                  (ByteString)+import qualified Data.ByteString                  as B+import           System.IO.Unsafe++newtype PitchTrack a = PitchTrack+    { unPitchTrack :: ReaderT (DywaPitchTrackPtr, Int) IO a+    } deriving (Functor, Applicative, Monad, MonadIO)++-- | Run the 'PitchTrack' monad+runPitchTrack :: Int -- ^ Number of samples to be used for each computation+    -> PitchTrack a -- ^ Computations+    -> IO a+runPitchTrack sampleNum f = withDywaPitchTrack $ \ptr -> do+    dywapitchInitTracking ptr+    runReaderT (unPitchTrack f) (ptr, sampleNum)++-- | Compute the pitch.+--+-- The size of the ByteString must be equal to+-- the number of samples set in 'runPitchTrack' * the size of each sample ('sampleSize').+--+-- Note: this pre-condition is __not__ checked!+computePitch :: ByteString -- ^ Samples+    -> PitchTrack Double -- ^ Computed pitch+computePitch rawSample = PitchTrack $ do+    (ptr, sampleNum) <- ask+    liftIO $ B.useAsCString rawSample $ \cString ->+        liftIO $ realToFrac <$>+        dywapitchComputePitch ptr (castToPtrDouble cString) 0 (fromIntegral sampleNum)++-- | Calculate the number of samples needed, based on the lowest frequency+neededSampleNum :: Int -- ^ Lowest frequency, in Hz+    -> Int -- ^ Number of samples needed for each computation+neededSampleNum n = unsafePerformIO $+    fromIntegral <$> dywapitchNeededSampleCount (fromIntegral n)
+ src/DywaPitchTrack/Internal/Bindings.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE EmptyDataDecls           #-}+{-# LANGUAGE ForeignFunctionInterface #-}+module DywaPitchTrack.Internal.Bindings (+    DywaPitchTrackPtr+  , SamplesPtr+  , castToPtrDouble+  , sampleSize+  , withDywaPitchTrack+  , dywapitchNeededSampleCount+  , dywapitchInitTracking+  , dywapitchComputePitch+  ) where++import           Foreign+import           Foreign.C.String+import           Foreign.C.Types++data DywaPitchTrack++type DywaPitchTrackPtr = Ptr DywaPitchTrack++type SamplesPtr = Ptr CDouble++castToPtrDouble :: CString -> Ptr CDouble+castToPtrDouble = castPtr++-- | Sample size+sampleSize :: Int+sampleSize = sizeOf (undefined :: CDouble)++withDywaPitchTrack :: (Ptr DywaPitchTrack -> IO a) -> IO a+withDywaPitchTrack = allocaBytes (sizeOf (undefined :: CInt) + sizeOf (undefined :: CDouble))++foreign import ccall safe "dywapitch_neededsamplecount"+    dywapitchNeededSampleCount :: CInt -> IO CInt++foreign import ccall safe "dywapitch_inittracking"+    dywapitchInitTracking :: Ptr DywaPitchTrack -> IO ()++foreign import ccall safe "dywapitch_computepitch"+    dywapitchComputePitch :: Ptr DywaPitchTrack -> Ptr CDouble -> CInt -> CInt -> IO CDouble