summaryrefslogtreecommitdiff
path: root/src/ios.m
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-21 13:46:57 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-21 13:46:57 +0300
commitbbbbc8e18879a5495be1cff78ca78be7a44ac25b (patch)
treecf24ab2e663e6a70250346328825f9c4a4eff45a /src/ios.m
parent8b12e6f56fddc4a10e74591ebf6d9af38a9f0bb5 (diff)
iOS: Background audio; MP3/AAC playback
Diffstat (limited to 'src/ios.m')
-rw-r--r--src/ios.m127
1 files changed, 127 insertions, 0 deletions
diff --git a/src/ios.m b/src/ios.m
index 58557ef5..ccf395fb 100644
--- a/src/ios.m
+++ b/src/ios.m
@@ -24,11 +24,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
24#include "app.h" 24#include "app.h"
25#include "ui/command.h" 25#include "ui/command.h"
26#include "ui/window.h" 26#include "ui/window.h"
27
28#include <the_Foundation/file.h>
29#include <the_Foundation/fileinfo.h>
30#include <the_Foundation/path.h>
27#include <SDL_events.h> 31#include <SDL_events.h>
28#include <SDL_syswm.h> 32#include <SDL_syswm.h>
33#include <SDL_timer.h>
29 34
30#import <UIKit/UIKit.h> 35#import <UIKit/UIKit.h>
31#import <CoreHaptics/CoreHaptics.h> 36#import <CoreHaptics/CoreHaptics.h>
37#import <AVFAudio/AVFAudio.h>
32 38
33static iBool isSystemDarkMode_ = iFalse; 39static iBool isSystemDarkMode_ = iFalse;
34static iBool isPhone_ = iFalse; 40static iBool isPhone_ = iFalse;
@@ -223,6 +229,7 @@ void setupApplication_iOS(void) {
223 selector:@selector(keyboardOffScreen:) 229 selector:@selector(keyboardOffScreen:)
224 name:UIKeyboardWillHideNotification 230 name:UIKeyboardWillHideNotification
225 object:nil]; 231 object:nil];
232 [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
226} 233}
227 234
228static iBool isDarkMode_(iWindow *window) { 235static iBool isDarkMode_(iWindow *window) {
@@ -309,3 +316,123 @@ void exportDownloadedFile_iOS(const iString *path) {
309 [appState_ setFileBeingSaved:path]; 316 [appState_ setFileBeingSaved:path];
310 [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; 317 [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil];
311} 318}
319
320/*----------------------------------------------------------------------------------------------*/
321
322enum iAVFAudioPlayerState {
323 initialized_AVFAudioPlayerState,
324 playing_AVFAudioPlayerState,
325 paused_AVFAudioPlayerState
326};
327
328struct Impl_AVFAudioPlayer {
329 iString cacheFilePath;
330 AVAudioPlayer *player;
331 float volume;
332 enum iAVFAudioPlayerState state;
333};
334
335iDefineTypeConstruction(AVFAudioPlayer)
336
337void init_AVFAudioPlayer(iAVFAudioPlayer *d) {
338 init_String(&d->cacheFilePath);
339 d->player = NULL;
340 d->volume = 1.0f;
341 d->state = initialized_AVFAudioPlayerState;
342}
343
344void deinit_AVFAudioPlayer(iAVFAudioPlayer *d) {
345 setInput_AVFAudioPlayer(d, NULL, NULL);
346}
347
348static const char *cacheDir_ = "~/Library/Caches/Audio";
349
350static const char *fileExt_(const iString *mimeType) {
351 /* Media types that AVFAudioPlayer will try to play. */
352 if (startsWithCase_String(mimeType, "audio/aiff") ||
353 startsWithCase_String(mimeType, "audio/x-aiff")) {
354 return ".aiff";
355 }
356 if (startsWithCase_String(mimeType, "audio/3gpp")) return ".3gpp";
357 if (startsWithCase_String(mimeType, "audio/mpeg")) return ".mp3";
358 if (startsWithCase_String(mimeType, "audio/mp3")) return ".mp3";
359 if (startsWithCase_String(mimeType, "audio/mp4")) return ".mp4";
360 if (startsWithCase_String(mimeType, "audio/mpeg4")) return ".mp4";
361 if (startsWithCase_String(mimeType, "audio/aac")) return ".aac";
362 return "";
363}
364
365iBool setInput_AVFAudioPlayer(iAVFAudioPlayer *d, const iString *mimeType, const iBlock *audioFileData) {
366 if (!isEmpty_String(&d->cacheFilePath)) {
367 remove(cstr_String(&d->cacheFilePath));
368 clear_String(&d->cacheFilePath);
369 }
370 if (d->player) {
371 d->player = nil;
372 }
373 if (mimeType && audioFileData && iCmpStr(fileExt_(mimeType), "")) {
374 makeDirs_Path(collectNewCStr_String(cacheDir_));
375 iFile *f = new_File(collectNewFormat_String("%s/%u%s", cacheDir_, SDL_GetTicks(), fileExt_(mimeType)));
376 if (open_File(f, writeOnly_FileMode)) {
377 write_File(f, audioFileData);
378 set_String(&d->cacheFilePath, path_File(f));
379 NSError *error = nil;
380 d->player = [[AVAudioPlayer alloc]
381 initWithContentsOfURL:[NSURL fileURLWithPath:
382 [NSString stringWithUTF8String:cstr_String(&d->cacheFilePath)]]
383 error:&error];
384 if (error) {
385 d->player = nil;
386 }
387 [d->player setVolume:d->volume];
388 }
389 iRelease(f);
390 }
391 return d->player != nil;
392}
393
394void play_AVFAudioPlayer(iAVFAudioPlayer *d) {
395 if (d->state != playing_AVFAudioPlayerState) {
396 [d->player play];
397 d->state = playing_AVFAudioPlayerState;
398 }
399}
400
401void stop_AVFAudioPlayer(iAVFAudioPlayer *d) {
402 [d->player stop];
403 d->state = initialized_AVFAudioPlayerState;
404}
405
406void setPaused_AVFAudioPlayer(iAVFAudioPlayer *d, iBool paused) {
407 if (paused && d->state != paused_AVFAudioPlayerState) {
408 [d->player pause];
409 d->state = paused_AVFAudioPlayerState;
410 }
411 else if (!paused && d->state != playing_AVFAudioPlayerState) {
412 [d->player play];
413 d->state = playing_AVFAudioPlayerState;
414 }
415}
416
417void setVolume_AVFAudioPlayer(iAVFAudioPlayer *d, float volume) {
418 d->volume = volume;
419 if (d->player) {
420 [d->player setVolume:volume];
421 }
422}
423
424double currentTime_AVFAudioPlayer(const iAVFAudioPlayer *d) {
425 return [d->player currentTime];
426}
427
428double duration_AVFAudioPlayer(const iAVFAudioPlayer *d) {
429 return [d->player duration];
430}
431
432iBool isStarted_AVFAudioPlayer(const iAVFAudioPlayer *d) {
433 return d->state != initialized_AVFAudioPlayerState;
434}
435
436iBool isPaused_AVFAudioPlayer(const iAVFAudioPlayer *d) {
437 return d->state == paused_AVFAudioPlayerState;
438}