Skip to content

Instantly share code, notes, and snippets.

@fjolnir
Created November 17, 2014 12:58
Show Gist options
  • Save fjolnir/221670a1bb8bd0b3fa85 to your computer and use it in GitHub Desktop.
Save fjolnir/221670a1bb8bd0b3fa85 to your computer and use it in GitHub Desktop.
#import "BPMDetector.h"
#import <Accelerate/Accelerate.h>
#import <aubio/aubio.h>
static uint_t const fftSize = 2048,
hopSize = fftSize/4;
@implementation BPMDetector {
AVCaptureSession *_captureSession;
AVCaptureAudioDataOutput *_dataOutput;
BPMDetectionBlock _handlerBlock;
dispatch_queue_t _detectionQueue;
aubio_tempo_t *_tempo;
}
+ (instancetype)bpmDetectorWithCaptureInput:(AVCaptureInput * const)aInput
{
NSParameterAssert(aInput);
BPMDetector *detector = [self new];
detector->_input = aInput;
detector->_dataOutput = [AVCaptureAudioDataOutput new];
[detector->_dataOutput setSampleBufferDelegate:detector queue:detector->_detectionQueue];
detector->_captureSession = [AVCaptureSession new];
[detector->_captureSession addInput:detector->_input];
[detector->_captureSession addOutput:detector->_dataOutput];
detector->_tempo = new_aubio_tempo("default", fftSize, hopSize, 44100);
return detector;
}
- (instancetype)init
{
if((self = [super init])) {
_detectionQueue = dispatch_queue_create([NSStringFromClass([self class]) UTF8String],
DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)dealloc
{
del_aubio_tempo(_tempo);
}
- (void)listenWithBlock:(BPMDetectionBlock const)aHandler
{
NSAssert(!_captureSession.isRunning, @"Detection already in progress!");
_handlerBlock = aHandler;
[_captureSession startRunning];
}
- (void)stopListening
{
[_captureSession stopRunning];
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
CMItemCount const sampleCount = CMSampleBufferGetNumSamples(sampleBuffer);
CMBlockBufferRef const audioBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
char *rawSampleData;
CMBlockBufferGetDataPointer(audioBuffer, 0, NULL, NULL, (char **)&rawSampleData);
CMAudioFormatDescriptionRef const format = CMSampleBufferGetFormatDescription(sampleBuffer);
const AudioStreamBasicDescription *streamDesc = CMAudioFormatDescriptionGetStreamBasicDescription(format);
NSAssert(streamDesc->mFormatID == kAudioFormatLinearPCM &&
streamDesc->mChannelsPerFrame == 1 &&
streamDesc->mBitsPerChannel == 16,
@"Unsupported audio format");
float * const samples = malloc(sampleCount * sizeof(float));
vDSP_vflt16((short *)rawSampleData, 1, samples, 1, sampleCount);
fvec_t * const inSampleVec = new_fvec(hopSize);
fvec_t * const beatSampleVec = new_fvec(2);
uint_t ofs = 0;
while(ofs < sampleCount) {
fvec_zeros(inSampleVec);
for(uint_t i = 0; i < MIN(hopSize, sampleCount-ofs); ++i) {
fvec_set_sample(inSampleVec, samples[ofs+i], i);
}
aubio_tempo_do(_tempo, inSampleVec, beatSampleVec);
if(beatSampleVec->data[0] != 0) {
smpl_t const bpm = aubio_tempo_get_bpm(_tempo);
smpl_t const confidence = aubio_tempo_get_confidence(_tempo);
dispatch_async(dispatch_get_main_queue(), ^{
if(_handlerBlock)
_handlerBlock(bpm, confidence);
});
}
ofs += hopSize;
}
del_fvec(inSampleVec);
del_fvec(beatSampleVec);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment