Пример обнаружения BPM в iOS SoundTouch
Я искал по всей сети и не могу найти учебник о том, как использовать библиотеку SoundTouch для обнаружения ударов.
(Примечание. До этого у меня не было опыта работы с C++. Я знаю C, Objective-C и Java. Поэтому я мог бы испортить кое-что из этого, но это компилируется.)
Я добавил фреймворк в свой проект и смог получить следующее для компиляции:
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
inputSamples(*samples, numSamples)
Информация о байте песни сбивает меня с толку.
Как я могу получить эти данные из файла песни?
Я пытался с помощью memcpy()
но это не похоже на работу.
У кого-нибудь есть мысли?
3 ответа
После долгих часов отладки и чтения ограниченной документации в Интернете, я изменил несколько вещей, прежде чем наткнуться на это: numSamples
от numberOfChannels
в inputSamples()
функция.
Мой окончательный код выглядит так:
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());
Для Swift 3:
https://github.com/Luccifer/BPM-Analyser
И используйте это как:
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)
Не стесняйтесь комментировать!
Я пробовал это решение для чтения BPM из mp3-файлов (используя класс TSLibraryImport для преобразования в wav) внутри музыкальной библиотеки iOS:
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());
}];
Странность в том, что рассчитанный BMP всегда имеет одно и то же значение:
2013-10-02 03:05:36.725 AppTestAudio[1464:1803] Beats Per Minute = 117.453835
Независимо от того, какая дорожка была, т.е. число кадров или размер буфера (здесь я использовал размер буфера 2K, как в примере SoundTouch в исходном коде библиотеки).