可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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());
回答2:
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 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).
回答3:
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!