Objective C Localized Audio Files not working - ios

I have been ploughing along with my learning of objective c and have got my app doing more or less everything I want it to do. I have the correct audio playing when certain buttons are pressed.
I have run in to some difficulty when localizing my audio file.
I have localized other elements of my app with no issue, for example I have used different images and button backgrounds and text by localizing in the same way but when I localize audio i run in to some issues and I can not find a solutions online as I normally do.
So as I said the issue only occurs when the file is localized, it effects the original language (where it was working before localizing the file) and the other languages (in this case French)
So the code I am using on my button is:
- (IBAction)domesticButtonClicked:(id)sender
{
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
resourcePath = [resourcePath stringByAppendingString:#"/localize-test.wav"];
// NSLog(#"Path to play: %#", resourcePath);
NSError* err;
//Initialize our player pointing to the path to our resource
domesticSound = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
if( err ){
//bail!
NSLog(#"Failed with reason: %#", [err localizedDescription]);
}
else{
//set our delegate and begin playback
domesticSound.delegate = self;
[domesticSound play];
domesticSound.numberOfLoops = 0;
domesticSound.currentTime = 0;
domesticSound.volume = 1.0;
}
}
}
So when this runs, instead of it playing the sound as it should I see the following in my output:
Failed with reason: The operation couldn’t be completed. (OSStatus error 2003334207.)
Can anyone shed any light on this for me?
Thanks in advance.
EDIT:
Thanks Marcus Adams, what you suggested does work but I can not use it on some buttons as i can not declare it every time.
So the reason the audio file is declared that way is so the audio file can be altered depending on the number of times the button has been clicked. Maybe this will help in getting my issue resolved.
- (IBAction)domestic03ButtonClicked:(id)sender
{
static int imageNumber = 0;
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
if (imageNumber == 0) {
domestic03ImageContainer.image = domestic300;
resourcePath = [resourcePath stringByAppendingString:#"/smb_coin1.wav"];
imageNumber++;
}
else if (imageNumber == 1) {
domestic03ImageContainer.image = domestic301;
resourcePath = [resourcePath stringByAppendingString:#"/smb_coin2.wav"];
imageNumber++;
}
else if (imageNumber == 2) {
domestic03ImageContainer.image = domestic302;
resourcePath = [resourcePath stringByAppendingString:#"/smb_coin3.wav"];
imageNumber++;
}
else if (imageNumber == 3) {
domestic03ImageContainer.image = domestic303;
resourcePath = [resourcePath stringByAppendingString:#"/smb_coin4.wav"];
imageNumber++;
}
else if (imageNumber == 4) {
domestic03ImageContainer.image = domestic304;
resourcePath = [resourcePath stringByAppendingString:#"/smb_coin5.wav"];
imageNumber = 0;
}
// NSLog(#"Path to play: %#", resourcePath);
NSError* err;
//Initialize our player pointing to the path to our resource
domesticSound = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
if( err ){
//bail!
NSLog(#"Failed with reason: %#", [err localizedDescription]);
}
else{
//set our delegate and begin playback
domesticSound.delegate = self;
[domesticSound play];
domesticSound.numberOfLoops = 0;
domesticSound.currentTime = 0;
domesticSound.volume = 1.0;
}
domestic03ImageContainer.Alpha = 1;
}

You should be able to use just this for your path, assuming you've put the resource directly in the proper localized folder:
NSString* resourcePath = [[NSBundle mainBundle] pathForResource:#"localize-test"
ofType:#"wav"];

You should only check err if domesticSound == nil. This is generally true for APIs that take an NSError** in/out argument. (err is not guaranteed to be written to upon success.)
You should also initialize err to nil!

Related

File written while app is in the background is not saved

I'm developing an app that can receive messages/files while it's on the background via webRTC.
When a file is received, I write it to disk. But when trying to access it later (even between app launches) that file doesn't exist.
User's document folder has NSFileProtectionCompleteUntilFirstUserAuthentication attribute.
I've tried to create the file on disk using NSData's [writeToURL:options:error:], [writeToFile:options:error:]; NSFileManager's [createFileAtPath:contents:attributes:] and also NSFileHandle's methods.
All of them successfully create the file at the designated path/url. Right after creation I check whether file exists with NSFileManager's [attributesOfItemAtPath:error:] which shows me the following:
attributes: {
NSFileCreationDate = "2018-05-07 18:47:50 +0000";
NSFileExtensionHidden = 0;
NSFileGroupOwnerAccountID = 501;
NSFileGroupOwnerAccountName = mobile;
NSFileModificationDate = "2018-05-07 18:47:50 +0000";
NSFileOwnerAccountID = 501;
NSFileOwnerAccountName = mobile;
NSFilePosixPermissions = 420;
NSFileProtectionKey = NSFileProtectionCompleteUntilFirstUserAuthentication;
NSFileReferenceCount = 1;
NSFileSize = 92156;
NSFileSystemFileNumber = 695101;
NSFileSystemNumber = 16777219;
NSFileType = NSFileTypeRegular;
}
[[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]] also shows me that the file exists after write.
Considering it could be a threading problem, I've also tried to write that file putting it on a main thread block, but result is the same. First file seems to be written but, when trying to access it afterwards it's like it never was.
Is there anything I could be missing?
edit: Added function I use to write.
- (void) saveFileData:(NSData *)fileData completionHandler:(void(^)(BOOL success))completionHandler {
NSURL *fileURL = [self fileURL];
NSError *error = nil;
[fileData writeToURL:fileURL options:NSDataWritingAtomic error:&error];
if (error) {
ZLogError(ZLogTypeFile, #"[%#] could not be saved: %#", self.fileKey, error);
completionHandler(NO);
return;
}
ZLogDebug(ZLogTypeFile, #"<file: %#> exists after write:%d", fileURL, [[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]);
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path] error:&error];
ZLogDebug(ZLogTypeFile, #"attributes: %#", attributes);
completionHandler(YES);
}
output comes as (where Documents is the users NSDocumentDirectory in the app)
[file: /Documents/57/Downloads/Images/9d1687ab5f4374a2c00429a24316b5ccd3fb0a67.png] exists after write:1
and getting the file (an image):
- (UIImage *) imageFromURL:(NSURL *)imageURL {
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:[imageURL path]];
if (!fileExists) {
ZLogDebug(ZLogTypeFile, #"[file: %#] exists: %d", imageURL, fileExists);
return nil;
}
return [UIImage imageWithData:[NSData dataWithContentsOfURL:imageURL]];
}
in log (note I've taken out the long path before Documents directory just here):
[file: /Documents/57/Downloads/Images/9d1687ab5f4374a2c00429a24316b5ccd3fb0a67.png] exists: 1

Multiple MIDI sounds with MusicDeviceMIDIEvent

I am creating MIDI sounds with MusicDeviceMIDIEvent and it's perfectly fine, I use some aupreset files I've created. One for each instrument.
My code is basically the example from apple: LoadPresetDemo. All I use is:
- (void)loadPreset:(id)sender instrumentName:(const char *)instrumentName {
NSString *presetURL1 = [NSString stringWithCString:instrumentName encoding:NSUTF8StringEncoding];
NSString *path = [[NSBundle mainBundle] pathForResource:presetURL1 ofType:#"aupreset"];
NSURL *presetURL = [[NSURL alloc] initFileURLWithPath: path];
if (presetURL) {
NSLog(#"Attempting to load preset '%#'\n", [presetURL description]);
}
else {
NSLog(#"COULD NOT GET PRESET PATH!");
}
[self loadSynthFromPresetURL: presetURL];
}
To load my aupreset file, then:
- (void) noteOn:(id)sender midiNumber:(int)midiNumber {
UInt32 noteNum = midiNumber;
UInt32 onVelocity = 127;
UInt32 noteCommand = kMIDIMessage_NoteOn << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent (self.samplerUnit, noteCommand, noteNum, onVelocity, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to start playing the low note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
to play the note of the previously loaded aupreset, and:
- (void) noteOff:(id)sender midiNumber:(int)midiNumber
when I want it to stop.
Now I would like to play one note of each instrument simultaneously. What is the easiest way of doing this?

Make my background music player stop? Objective C

I have several views, one of them is an "options" view, in this view there will be a button that will allow the user to click a button to "stop" the background music, or click it to turn it back on. Im pretty sure I have the general idea of how to do this, the problem is, my code isnt working and I am not sure why.
this is the code from the implementation file for the "options" class:
- (IBAction)musicIO:(id)sender {
MenuViewController *myMusicPlayer = [[MenuViewController alloc] init];
[myMusicPlayer backgroundMusicStart];
}
it calls the method in my menu code(a different class)
This is my menu code:
-(IBAction) backgroundMusicStart{
if( i != 1){
NSURL * URL = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/backgroundMusic.wav", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
backgroundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:URL error:&error];
backgroundPlayer.numberOfLoops = -1;
[backgroundPlayer play];
i = 1;
NSLog(#"In start music");
}
else if ( i == 1 ) {
[backgroundPlayer stop];
i = 0;
NSLog(#"In stop music");
}
}
"i" is a globally declared variable.
The problem with the code:
it runs, NSLog outputs that it gets into both if statements fine, the problem is when it is in the [backgroundPlayer stop] bracket, it doesnt stop the music, and I cant figure out why.
You're initialising backgroundPlayer every time this method is invoked and in only one of the cases. What you should be doing is make backgroundPlayer an instance variable and act on the shared instance of it... Example follows:
// static AVPlayer *backgroundPlayer;
- (IBAction) backgroundMusicStart {
if (backgroundPlayer == nil) {
backgroundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:URL error:&error];
backgroundPlayer.numberOfLoops = -1;
}
if (i == 1) { [backgroundPlayer stop]; }
else if (i == 0) { backgroundPlayer play]; }
}

OpenCV VideoCapture not opens file ios

I've saved a video to a file using:
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
documentsDirectory = [documentsDirectory stringByAppendingPathComponent:#"test9.avi"];
NSLog(#"Open to: %#", documentsDirectory);
std::string fileName([documentsDirectory cString], [documentsDirectory cStringLength]);
videoWriter = new cv::VideoWriter(fileName, CV_FOURCC('H', 'F', 'Y', 'U'), 25, cvSize(288,352),1);
But I can't open this with VideoCapture:
NSLog(#"Load the video");
videoCapture = new cv::VideoCapture();
NSLog(#"Video loaded");
if (videoCapture->open(fileName))
{
NSLog(#"Opened");
}
else {
NSLog(#"Failed to open");
}
There is no error from the VideoCapture itself just my log "Failed to open" ...
Can I somehow get more detailed error log?
cv::VideoCapture capture;
NSString* pathToInputFile = [NSString documentPathByAppendingPath:#"Video/sample_iTunes.mov"];
if(capture.open(std::string([pathToFile UTF8String]))){
NSLog(#"Opened");
}else{
NSLog(#"Failed to open");
}
works fine for me
I met this problem using Swift 3.
Thanks to answers by Kirill and CodeBender, I solved this.
let resourceURL = Bundle.main.url(forResource: "test", withExtension: "MOV")
let openCVVideoCapture = OpenCVVideoCaptureWrapper.init(file: resourceURL?.path)
and following is my OpenCVVideoCaptureWrapper.mm
#implementation OpenCVVideoCaptureWrapper {
cv::VideoCapture video;
}
- (instancetype)initWithFile:(NSString*) filePath {
std::string cvFilePath = [filePath UTF8String];
video = cv::VideoCapture(cvFilePath);
if (!video.isOpened()) NSLog(#"Could not open file");
return self;
}
#end
This snippet worked for me in Xcode 10, Swift 4. Thanks to #Nathan Tuggy.
- (void)openVideoFile:(NSURL *)fileName {
NSString *filename = fileName.path;
std::string filePath = [filename UTF8String];
cv::VideoCapture capture(filePath);
if (!capture.isOpened()) {
printf("#Error Opening video file");
// failed, print error message
}
else {
printf("File Opened");
}
}
While Kirill's answer would have worked when it was posted, that is no longer the case now.
The following works in Xcode 8.2.1, replacing the documentPathByAppendingPath function that is no longer available.
cv::VideoCapture cap;
NSString *path = [[NSBundle mainBundle] pathForResource:#"MyVideo" ofType:#"MOV"];
if ( cap.open ( std::string( path.UTF8String ) ) ) {
NSLog(#"Opened");
} else {
NSLog(#"Failed to open");
}

Playing midi files with MusicPlayer & Music Sequence

I've successfully gotten iOS to play a .mid (midi) file with a soundfont sample using the following code:
-(void) playMusic:(NSString*) name
{
NSString *presetURLPath = [[NSBundle mainBundle] pathForResource:#"GortsMiniPianoJ1" ofType:#"SF2"];
NSURL * presetURL = [NSURL fileURLWithPath:presetURLPath];
[self loadFromDLSOrSoundFont: (NSURL *)presetURL withPatch: (int)3];
NSString *midiFilePath = [[NSBundle mainBundle] pathForResource:name ofType:#"mid"];
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
NewMusicPlayer(&musicPlayer);
if (NewMusicSequence(&musicSequence) != noErr)
{
[NSException raise:#"play" format:#"Can't create MusicSequence"];
}
if(MusicSequenceFileLoad(musicSequence, (CFURLRef)midiFileURL, 0, 0 != noErr))
{
[NSException raise:#"play" format:#"Can't load MusicSequence"];
}
MusicPlayerSetSequence(musicPlayer, musicSequence);
MusicSequenceSetAUGraph(musicSequence, _processingGraph);
MusicPlayerPreroll(musicPlayer);
MusicPlayerStart(musicPlayer);
}
However, the problem comes when I then try to play a second file when the first is still playing.
I've tried many variations. Firstly, the above code will play both tracks simultaneously. Or, I've tried:
DisposeMusicPlayer(musicPlayer);
DisposeMusicSequence(musicSequence);
Before the NewMusicPlayer(&musicPlayer), but this produces a weird version of the tune with only sporadic notes being played.
I'd love to simply call this method, and the next track to be played.
Ok, I found the answer on how to properly dispose of a MusicPlayer and MusicSequence.
-(void) stop
{
OSStatus result = noErr;
result = MusicPlayerStop(musicPlayer);
UInt32 trackCount;
MusicSequenceGetTrackCount(musicSequence, &trackCount);
MusicTrack track;
for(int i=0;i<trackCount;i++)
{
MusicSequenceGetIndTrack (musicSequence, i, &track);
result = MusicSequenceDisposeTrack(musicSequence, track);
}
result = DisposeMusicPlayer(musicPlayer);
result = DisposeMusicSequence(musicSequence);
result = DisposeAUGraph(_processingGraph);
}

Resources