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 +21/−0
- Setup.hs +2/−0
- cbits/dywapitchtrack.c +448/−0
- dywapitchtrack.cabal +48/−0
- include/dywapitchtrack.h +110/−0
- src/DywaPitchTrack.hs +62/−0
- src/DywaPitchTrack/Internal/Bindings.hs +41/−0
+ 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 = "&ltitudeThreshold+ //--+ }+ }+ + 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 = "&ltitudeThreshold+ //--+ }+ }+ }+ + 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