Question

I have searched all over the web and cannot find a tutorial on how to use the SoundTouch library for beat detection.

(Note: I have no C++ experience prior to this. I do know C, Objective-C, and Java. So I could have messed some of this up, but it compiles.)

I added the framework to my project and managed to get the following to compile:

NSString *path = [[NSBundle mainBundle] pathForResource:@"song" ofType:@"wav"];

NSData *data = [NSData dataWithContentsOfFile:path];

player =[[AVAudioPlayer alloc] initWithData:data error:NULL];

player.volume = 1.0;

player.delegate = self;

[player prepareToPlay];
[player play];

NSUInteger len = [player.data length]; // Get the length of the data

soundtouch::SAMPLETYPE sampleBuffer[len]; // Create buffer array

[player.data getBytes:sampleBuffer length:len]; // Copy the bytes into the buffer

soundtouch::BPMDetect *BPM = new soundtouch::BPMDetect(player.numberOfChannels, [[player.settings valueForKey:@"AVSampleRateKey"] longValue]); // This is working (tested)

BPM->inputSamples(sampleBuffer, len); // Send the samples to the BPM class

NSLog(@"Beats Per Minute = %f", BPM->getBpm()); // Print out the BPM - currently returns 0.00 for errors per documentation

The inputSamples(*samples, numSamples) song byte information confuses me.

How do I get these pieces of information from a song file?

I tried using memcpy() but it doesn't seem to be working.

Anyone have any thoughts?

Was it helpful?

Solution

After hours and hours of debugging and reading the limited documentation on the web, I modified a few things before stumbling upon this: You need to divide numSamples by numberOfChannels in the inputSamples() function.

My final code is like so:

NSString *path = [[NSBundle mainBundle] pathForResource:@"song" ofType:@"wav"];

NSData *data = [NSData dataWithContentsOfFile:path];

player =[[AVAudioPlayer alloc] initWithData:data error:NULL];

player.volume = 1.0;    // optional to play music

player.delegate = self;

[player prepareToPlay]; // optional to play music
[player play];          // optional to play music

NSUInteger len = [player.data length];

soundtouch::SAMPLETYPE sampleBuffer[len];

[player.data getBytes:sampleBuffer length:len];

soundtouch::BPMDetect BPM(player.numberOfChannels, [[player.settings valueForKey:@"AVSampleRateKey"] longValue]);

BPM.inputSamples(sampleBuffer, len/player.numberOfChannels);

NSLog(@"Beats Per Minute = %f", BPM.getBpm());

OTHER TIPS

I've tried this solution to read the BPM from mp3 files (using the TSLibraryImport class to convert to wav) inside the iOS Music Library:

                                MPMediaItem *item = [collection representativeItem];

                                 NSURL *urlStr = [item valueForProperty:MPMediaItemPropertyAssetURL];

                                 TSLibraryImport* import = [[TSLibraryImport alloc] init];

                                 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
                                 NSString *documentsDirectory = [paths objectAtIndex:0];

                                 NSURL* destinationURL = [NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:@"temp_data"]];
                                 [[NSFileManager defaultManager] removeItemAtURL:destinationURL error:nil];

                                 [import importAsset:urlStr toURL:destinationURL completionBlock:^(TSLibraryImport* import) {

                                     NSString *outPath = [documentsDirectory stringByAppendingPathComponent:@"temp_data"];


                                     NSData *data = [NSData dataWithContentsOfFile:outPath];
                                     AVAudioPlayer *player =[[AVAudioPlayer alloc] initWithData:data error:NULL];

                                     NSUInteger len = [player.data length];
                                     int numChannels = player.numberOfChannels;

                                     soundtouch::SAMPLETYPE sampleBuffer[1024];

                                     soundtouch::BPMDetect *BPM = new soundtouch::BPMDetect(player.numberOfChannels, [[player.settings valueForKey:@"AVSampleRateKey"] longValue]);


                                     for (NSUInteger i = 0; i <= len - 1024; i = i + 1024) {

                                         NSRange r = NSMakeRange(i, 1024);
                                         //NSData *temp = [player.data subdataWithRange:r];
                                         [player.data getBytes:sampleBuffer range:r];

                                         int samples = sizeof(sampleBuffer) / numChannels;

                                         BPM->inputSamples(sampleBuffer, samples); // Send the samples to the BPM class

                                     }

                                     NSLog(@"Beats Per Minute = %f", BPM->getBpm());


                                 }];

The strangeness is that the calculated BMP is always the same value:

2013-10-02 03:05:36.725 AppTestAudio[1464:1803]  Beats Per Minute = 117.453835

No matter which track was i.e. number of frames or the buffer size (here I used 2K buffer size as for the SoundTouch example in the source code of the library).

For Swift 3:

https://github.com/Luccifer/BPM-Analyser

And use it like:

guard let filePath = Bundle.main.path(forResource: "TestMusic", ofType: "m4a"),
let url = URL(string: filePath) else {return "error occured, check fileURL"}

BPMAnalyzer.core.getBpmFrom(url, completion: nil)

Feel free to comment!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top