iOS SoundTouch framework song BPM Detection example - ios

I am still looking for a working code which calculates BPM, did any of you solve this code? Appreciate any help.
This is the code I have and it returns either 0 value or wrong value (compared with BPM value online)
-(void) calcBPM {
NSString *path = [[NSBundle mainBundle] pathForResource:#"song_test" ofType:#"mp3"];
NSData *data = [NSData dataWithContentsOfFile:path];
AVAudioPlayer *player =[[AVAudioPlayer alloc] initWithData:data error:NULL];
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());
}

Related

Best way to save lots of streaming data from iPhone

Currently, I am streaming image and depth data from my iphone X and doing a series of computations on it using a compute metal kernel. The output every cycle (frame) is approximately 900,000 floats. Assuming there are 30 cycles (frames) a second, this represents 27million floats a second.
I would like to save a few seconds (3-5) of this data to a file, which I can then access later.
My current approach of writing the output every cycle is too slow (I/O is expensive) and I am not sure how to buffer the results of a few cycles correctly (I tried creating an array of arrays but then run into memory issues).
Copy data from mtl buffers:
//number of elements per frame approx 300,000
size_t len = elementCount
finalArrayX = new float [len];
finalArrayY = new float [len];
finalArrayZ = new float [len];
//MTLBuffers
finalArrayX = (float*) resultsBufferX.contents;
finalArrayY = (float*) resultsBufferY.contents;
finalArrayZ = (float*) resultsBufferZ.contents;
writeToFile(finalArrayX, finalArrayY, finalArrayZ)
writeToFile:
/*
* Get file handle to append to
*/
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *documentTXTPath = [documentsDirectory stringByAppendingPathComponent:#"test.txt"];
if(![[NSFileManager defaultManager] fileExistsAtPath: documentTXTPath]) {
[[NSFileManager defaultManager] createFileAtPath: documentTXTPath contents:nil attributes:nil];
}
NSFileHandle *myHandle = [NSFileHandle fileHandleForWritingAtPath:documentTXTPath];
[myHandle seekToEndOfFile];
/*
* Create strings and write line by line
*/
for(int k = 0; k < elementCount; k ++){
NSString *x_i = [NSString stringWithFormat:#"%.2f", finalArrayX[k]];
NSString *y_i = [NSString stringWithFormat:#"%.2f", finalArrayY[k]];
NSString *z_i = [NSString stringWithFormat:#"%.2f", finalArrayZ[k]];
NSString *temp = [line stringByAppendingString: x_i];
temp = [temp stringByAppendingString: #" "];
NSString *tempTwo = [temp stringByAppendingString: y_i];
tempTwo = [tempTwo stringByAppendingString: #" "];
NSString *tempThree = [tempTwo stringByAppendingString: z_i];
NSString *totalLine = [tempThree stringByAppendingString: #"\n"];
[myHandle writeData:[totalLine dataUsingEncoding:NSUTF8StringEncoding]];
}
Whats the best way to go about this?

iOS SoundTouch framework BPM Detection example

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?
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());
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!

addSkipBackupAttributeToItemAtURL -> NSString parameter?

In order to follow the Data Storage Guidelines I must use the below method to add a flag to say to not back it up to iCloud. However, the parameter here is for a NSURL. I need to pass it a NSString like from a line like so
return [[self offlineQueuePath] stringByAppendingPathComponent:#"SHKOfflineQueue.plist"];
Here is the method that takes in a URL.
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
} else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}
Anyway, how would I modify the method above to achieve the same while taking in a NSString as a parameter?
Thanks!
You don't need to modify the method. Convert your string to URL.
NSURL *url = [NSURL URLWithString:#"your string"];
Use this method
NSURL *pathURL113= [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#",Your string]];
this is perfect code.

I'm trying to find a file size. But my length is equl to zero

I'm trying to find a file size.
NSFileHandle *output = [NSFileHandle fileHandleForWritingAtPath:self.finalPath];
//seek to begin of the file
[output seekToFileOffset:0];
NSData *mydata = [output availableData];
NSLog(#"length: %d", [mydata length]);
But my length is equl to zero. Why?
availableData is for reading files. If you just want to find out the size of a file, you don't actually have to open it. Just use NSFileManager like this:
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.finalPath error:NULL];
unsigned long long fileSize = [attributes fileSize]; // in bytes
[fileHandler seekToEndOfFile], this returns the length of your file data, your code [output seekToFileOffset:0] means from 0-0,so there is no data

Having an issue in encrypting/decrypting field in SQLite3

I am trying to encrypt/decrypt one field of SQLite3 database stored in iPhone app.
I am using this category mentioned in this question.
While encrypting, I am using following code:
NSString *key = #"pass123";
NSString *secret = webNote.note;
NSData *plain = [secret dataUsingEncoding:NSUTF8StringEncoding];
NSData *cipher = [plain AES256EncryptWithKey:key];
sqlite3_bind_text(statement, 1, [[cipher description] UTF8String], -1, SQLITE_TRANSIENT);
It does save data into the field in 74657874 20746f20 656e6372 797074 format.
But while decrypting, I get blank field (tried everything I knew). I am using following code for decrypting:
char *noteDet = (char *)sqlite3_column_text(statement, 1);
NSString *key = #"pass123";
NSString *secret = [NSString stringWithUTF8String:noteDet];
NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];
NSData *clean = [secretData AES256DecryptWithKey:key];
aNote.note = ([[NSString alloc] initWithData:clean encoding:NSUTF8StringEncoding])?[[NSString alloc] initWithData:clean encoding:NSUTF8StringEncoding]:#"";
I think, I am unable to convert types. Please guide!
Thanks!
The description of NSData returns something like "" => if you were to read that again you would get different overall data ( and of a higher length ), also you are right that you are not converting the data types correctly.
Try saving the NSData object directly, by saving the bytes themselves rather than the description of the NSData object.
void *bytes = [dataObject bytes];
size_t length = [dataObject length];

Resources