diff options
-rw-r--r-- | Depends-iOS.cmake | 1 | ||||
-rw-r--r-- | res/iOSBundleInfo.plist.in | 4 | ||||
-rw-r--r-- | src/audio/player.c | 87 | ||||
-rw-r--r-- | src/audio/player.h | 30 | ||||
-rw-r--r-- | src/ios.h | 16 | ||||
-rw-r--r-- | src/ios.m | 127 | ||||
-rw-r--r-- | src/media.c | 6 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 3 | ||||
-rw-r--r-- | src/ui/window.c | 1 |
9 files changed, 247 insertions, 28 deletions
diff --git a/Depends-iOS.cmake b/Depends-iOS.cmake index 013ee09a..c3747769 100644 --- a/Depends-iOS.cmake +++ b/Depends-iOS.cmake | |||
@@ -7,6 +7,7 @@ set (SDL2_LDFLAGS | |||
7 | ${IOS_DIR}/lib/libSDL2.a | 7 | ${IOS_DIR}/lib/libSDL2.a |
8 | "-framework AudioToolbox" | 8 | "-framework AudioToolbox" |
9 | "-framework AVFoundation" | 9 | "-framework AVFoundation" |
10 | "-framework AVFAudio" | ||
10 | "-framework CoreAudio" | 11 | "-framework CoreAudio" |
11 | "-framework CoreGraphics" | 12 | "-framework CoreGraphics" |
12 | "-framework CoreHaptics" | 13 | "-framework CoreHaptics" |
diff --git a/res/iOSBundleInfo.plist.in b/res/iOSBundleInfo.plist.in index 18fdc4a3..1124c078 100644 --- a/res/iOSBundleInfo.plist.in +++ b/res/iOSBundleInfo.plist.in | |||
@@ -49,6 +49,10 @@ | |||
49 | </array> | 49 | </array> |
50 | <key>UILaunchStoryboardName</key> | 50 | <key>UILaunchStoryboardName</key> |
51 | <string>LaunchScreen</string> | 51 | <string>LaunchScreen</string> |
52 | <key>UIBackgroundModes</key> | ||
53 | <array> | ||
54 | <string>audio</string> | ||
55 | </array> | ||
52 | <key>ITSAppUsesNonExemptEncryption</key> | 56 | <key>ITSAppUsesNonExemptEncryption</key> |
53 | <false/> | 57 | <false/> |
54 | <key>LSSupportsOpeningDocumentsInPlace</key> | 58 | <key>LSSupportsOpeningDocumentsInPlace</key> |
diff --git a/src/audio/player.c b/src/audio/player.c index 35786284..20b40f73 100644 --- a/src/audio/player.c +++ b/src/audio/player.c | |||
@@ -33,11 +33,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
33 | #include <SDL_timer.h> | 33 | #include <SDL_timer.h> |
34 | 34 | ||
35 | #if defined (LAGRANGE_ENABLE_MPG123) | 35 | #if defined (LAGRANGE_ENABLE_MPG123) |
36 | # include <mpg123.h> | 36 | # include <mpg123.h> |
37 | #endif | ||
38 | #if defined (iPlatformAppleMobile) | ||
39 | # include "../ios.h" | ||
37 | #endif | 40 | #endif |
38 | 41 | ||
39 | /*----------------------------------------------------------------------------------------------*/ | 42 | /*----------------------------------------------------------------------------------------------*/ |
40 | 43 | ||
44 | iDeclareType(AVFAudioPlayer) /* iOS */ | ||
45 | |||
41 | iDeclareType(ContentSpec) | 46 | iDeclareType(ContentSpec) |
42 | 47 | ||
43 | enum iDecoderType { | 48 | enum iDecoderType { |
@@ -447,6 +452,7 @@ struct Impl_Player { | |||
447 | iInputBuf * data; | 452 | iInputBuf * data; |
448 | uint32_t lastInteraction; | 453 | uint32_t lastInteraction; |
449 | iDecoder * decoder; | 454 | iDecoder * decoder; |
455 | iAVFAudioPlayer * avfPlayer; /* iOS */ | ||
450 | }; | 456 | }; |
451 | 457 | ||
452 | iDefineTypeConstruction(Player) | 458 | iDefineTypeConstruction(Player) |
@@ -634,24 +640,40 @@ static void writeOutputSamples_Player_(void *plr, Uint8 *stream, int len) { | |||
634 | void init_Player(iPlayer *d) { | 640 | void init_Player(iPlayer *d) { |
635 | iZap(d->spec); | 641 | iZap(d->spec); |
636 | init_String(&d->mime); | 642 | init_String(&d->mime); |
637 | d->device = 0; | 643 | d->device = 0; |
638 | d->decoder = NULL; | 644 | d->decoder = NULL; |
639 | d->data = new_InputBuf(); | 645 | d->avfPlayer = NULL; |
640 | d->volume = 1.0f; | 646 | d->data = new_InputBuf(); |
641 | d->flags = 0; | 647 | d->volume = 1.0f; |
648 | d->flags = 0; | ||
642 | } | 649 | } |
643 | 650 | ||
644 | void deinit_Player(iPlayer *d) { | 651 | void deinit_Player(iPlayer *d) { |
645 | stop_Player(d); | 652 | stop_Player(d); |
646 | delete_InputBuf(d->data); | 653 | delete_InputBuf(d->data); |
647 | deinit_String(&d->mime); | 654 | deinit_String(&d->mime); |
655 | #if defined (iPlatformAppleMobile) | ||
656 | if (d->avfPlayer) { | ||
657 | delete_AVFAudioPlayer(d->avfPlayer); | ||
658 | } | ||
659 | #endif | ||
648 | } | 660 | } |
649 | 661 | ||
650 | iBool isStarted_Player(const iPlayer *d) { | 662 | iBool isStarted_Player(const iPlayer *d) { |
663 | #if defined (iPlatformAppleMobile) | ||
664 | if (d->avfPlayer) { | ||
665 | return isStarted_AVFAudioPlayer(d->avfPlayer); | ||
666 | } | ||
667 | #endif | ||
651 | return d->device != 0; | 668 | return d->device != 0; |
652 | } | 669 | } |
653 | 670 | ||
654 | iBool isPaused_Player(const iPlayer *d) { | 671 | iBool isPaused_Player(const iPlayer *d) { |
672 | #if defined (iPlatformAppleMobile) | ||
673 | if (d->avfPlayer) { | ||
674 | return isPaused_AVFAudioPlayer(d->avfPlayer); | ||
675 | } | ||
676 | #endif | ||
655 | if (!d->device) return iTrue; | 677 | if (!d->device) return iTrue; |
656 | return SDL_GetAudioDeviceStatus(d->device) == SDL_AUDIO_PAUSED; | 678 | return SDL_GetAudioDeviceStatus(d->device) == SDL_AUDIO_PAUSED; |
657 | } | 679 | } |
@@ -676,15 +698,26 @@ void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock * | |||
676 | case append_PlayerUpdate: { | 698 | case append_PlayerUpdate: { |
677 | const size_t oldSize = size_Block(&input->data); | 699 | const size_t oldSize = size_Block(&input->data); |
678 | const size_t newSize = size_Block(data); | 700 | const size_t newSize = size_Block(data); |
679 | iAssert(newSize >= oldSize); | 701 | if (input->isComplete) { |
702 | iAssert(newSize == oldSize); | ||
703 | break; | ||
704 | } | ||
680 | /* The old parts cannot have changed. */ | 705 | /* The old parts cannot have changed. */ |
681 | // iAssert(memcmp(constData_Block(&input->data), constData_Block(data), oldSize) == 0); | ||
682 | appendData_Block(&input->data, constBegin_Block(data) + oldSize, newSize - oldSize); | 706 | appendData_Block(&input->data, constBegin_Block(data) + oldSize, newSize - oldSize); |
683 | input->isComplete = iFalse; | ||
684 | break; | 707 | break; |
685 | } | 708 | } |
686 | case complete_PlayerUpdate: | 709 | case complete_PlayerUpdate: |
687 | input->isComplete = iTrue; | 710 | if (!input->isComplete) { |
711 | input->isComplete = iTrue; | ||
712 | #if defined (iPlatformAppleMobile) | ||
713 | iAssert(d->avfPlayer == NULL); | ||
714 | d->avfPlayer = new_AVFAudioPlayer(); | ||
715 | if (!setInput_AVFAudioPlayer(d->avfPlayer, &d->mime, &input->data)) { | ||
716 | delete_AVFAudioPlayer(d->avfPlayer); | ||
717 | d->avfPlayer = NULL; | ||
718 | } | ||
719 | #endif | ||
720 | } | ||
688 | break; | 721 | break; |
689 | } | 722 | } |
690 | signal_Condition(&input->changed); | 723 | signal_Condition(&input->changed); |
@@ -695,6 +728,13 @@ iBool start_Player(iPlayer *d) { | |||
695 | if (isStarted_Player(d)) { | 728 | if (isStarted_Player(d)) { |
696 | return iFalse; | 729 | return iFalse; |
697 | } | 730 | } |
731 | #if defined (iPlatformAppleMobile) | ||
732 | if (d->avfPlayer) { | ||
733 | play_AVFAudioPlayer(d->avfPlayer); | ||
734 | setNotIdle_Player(d); | ||
735 | return iTrue; | ||
736 | } | ||
737 | #endif | ||
698 | iContentSpec content = contentSpec_Player_(d); | 738 | iContentSpec content = contentSpec_Player_(d); |
699 | if (!content.output.freq) { | 739 | if (!content.output.freq) { |
700 | return iFalse; | 740 | return iFalse; |
@@ -713,6 +753,12 @@ iBool start_Player(iPlayer *d) { | |||
713 | } | 753 | } |
714 | 754 | ||
715 | void setPaused_Player(iPlayer *d, iBool isPaused) { | 755 | void setPaused_Player(iPlayer *d, iBool isPaused) { |
756 | #if defined (iPlatformAppleMobile) | ||
757 | if (d->avfPlayer) { | ||
758 | setPaused_AVFAudioPlayer(d->avfPlayer, isPaused); | ||
759 | return; | ||
760 | } | ||
761 | #endif | ||
716 | if (isStarted_Player(d)) { | 762 | if (isStarted_Player(d)) { |
717 | SDL_PauseAudioDevice(d->device, isPaused ? SDL_TRUE : SDL_FALSE); | 763 | SDL_PauseAudioDevice(d->device, isPaused ? SDL_TRUE : SDL_FALSE); |
718 | setNotIdle_Player(d); | 764 | setNotIdle_Player(d); |
@@ -720,6 +766,12 @@ void setPaused_Player(iPlayer *d, iBool isPaused) { | |||
720 | } | 766 | } |
721 | 767 | ||
722 | void stop_Player(iPlayer *d) { | 768 | void stop_Player(iPlayer *d) { |
769 | #if defined (iPlatformAppleMobile) | ||
770 | if (d->avfPlayer) { | ||
771 | stop_AVFAudioPlayer(d->avfPlayer); | ||
772 | return; | ||
773 | } | ||
774 | #endif | ||
723 | if (isStarted_Player(d)) { | 775 | if (isStarted_Player(d)) { |
724 | /* TODO: Stop the stream/decoder. */ | 776 | /* TODO: Stop the stream/decoder. */ |
725 | SDL_PauseAudioDevice(d->device, SDL_TRUE); | 777 | SDL_PauseAudioDevice(d->device, SDL_TRUE); |
@@ -735,6 +787,11 @@ void setVolume_Player(iPlayer *d, float volume) { | |||
735 | if (d->decoder) { | 787 | if (d->decoder) { |
736 | d->decoder->gain = d->volume; | 788 | d->decoder->gain = d->volume; |
737 | } | 789 | } |
790 | #if defined (iPlatformAppleMobile) | ||
791 | if (d->avfPlayer) { | ||
792 | setVolume_AVFAudioPlayer(d->avfPlayer, volume); | ||
793 | } | ||
794 | #endif | ||
738 | setNotIdle_Player(d); | 795 | setNotIdle_Player(d); |
739 | } | 796 | } |
740 | 797 | ||
@@ -762,11 +819,21 @@ const iString *tag_Player(const iPlayer *d, enum iPlayerTag tag) { | |||
762 | } | 819 | } |
763 | 820 | ||
764 | float time_Player(const iPlayer *d) { | 821 | float time_Player(const iPlayer *d) { |
822 | #if defined (iPlatformAppleMobile) | ||
823 | if (d->avfPlayer) { | ||
824 | return currentTime_AVFAudioPlayer(d->avfPlayer); | ||
825 | } | ||
826 | #endif | ||
765 | if (!d->decoder) return 0; | 827 | if (!d->decoder) return 0; |
766 | return (float) ((double) d->decoder->currentSample / (double) d->spec.freq); | 828 | return (float) ((double) d->decoder->currentSample / (double) d->spec.freq); |
767 | } | 829 | } |
768 | 830 | ||
769 | float duration_Player(const iPlayer *d) { | 831 | float duration_Player(const iPlayer *d) { |
832 | #if defined (iPlatformAppleMobile) | ||
833 | if (d->avfPlayer) { | ||
834 | return duration_AVFAudioPlayer(d->avfPlayer); | ||
835 | } | ||
836 | #endif | ||
770 | if (!d->decoder) return 0; | 837 | if (!d->decoder) return 0; |
771 | return (float) ((double) d->decoder->totalSamples / (double) d->spec.freq); | 838 | return (float) ((double) d->decoder->totalSamples / (double) d->spec.freq); |
772 | } | 839 | } |
diff --git a/src/audio/player.h b/src/audio/player.h index 82d95fd2..8753d811 100644 --- a/src/audio/player.h +++ b/src/audio/player.h | |||
@@ -49,21 +49,21 @@ enum iPlayerTag { | |||
49 | void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data, | 49 | void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data, |
50 | enum iPlayerUpdate update); | 50 | enum iPlayerUpdate update); |
51 | 51 | ||
52 | iBool start_Player (iPlayer *); | 52 | iBool start_Player (iPlayer *); |
53 | void stop_Player (iPlayer *); | 53 | void stop_Player (iPlayer *); |
54 | void setPaused_Player (iPlayer *, iBool isPaused); | 54 | void setPaused_Player (iPlayer *, iBool isPaused); |
55 | void setVolume_Player (iPlayer *, float volume); | 55 | void setVolume_Player (iPlayer *, float volume); |
56 | void setFlags_Player (iPlayer *, int flags, iBool set); | 56 | void setFlags_Player (iPlayer *, int flags, iBool set); |
57 | void setNotIdle_Player (iPlayer *); | 57 | void setNotIdle_Player (iPlayer *); |
58 | 58 | ||
59 | int flags_Player (const iPlayer *); | 59 | int flags_Player (const iPlayer *); |
60 | const iString *tag_Player (const iPlayer *, enum iPlayerTag tag); | 60 | const iString *tag_Player (const iPlayer *, enum iPlayerTag tag); |
61 | iBool isStarted_Player (const iPlayer *); | 61 | iBool isStarted_Player (const iPlayer *); |
62 | iBool isPaused_Player (const iPlayer *); | 62 | iBool isPaused_Player (const iPlayer *); |
63 | float volume_Player (const iPlayer *); | 63 | float volume_Player (const iPlayer *); |
64 | float time_Player (const iPlayer *); | 64 | float time_Player (const iPlayer *); |
65 | float duration_Player (const iPlayer *); | 65 | float duration_Player (const iPlayer *); |
66 | float streamProgress_Player (const iPlayer *); /* normalized 0...1 */ | 66 | float streamProgress_Player (const iPlayer *); /* normalized 0...1 */ |
67 | 67 | ||
68 | uint32_t idleTimeMs_Player (const iPlayer *); | 68 | uint32_t idleTimeMs_Player (const iPlayer *); |
69 | iString * metadataLabel_Player (const iPlayer *); | 69 | iString * metadataLabel_Player (const iPlayer *); |
@@ -39,3 +39,19 @@ void exportDownloadedFile_iOS(const iString *path); | |||
39 | iBool isPhone_iOS (void); | 39 | iBool isPhone_iOS (void); |
40 | void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); | 40 | void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); |
41 | int displayRefreshRate_iOS (void); | 41 | int displayRefreshRate_iOS (void); |
42 | |||
43 | /*----------------------------------------------------------------------------------------------*/ | ||
44 | |||
45 | iDeclareType(AVFAudioPlayer) | ||
46 | iDeclareTypeConstruction(AVFAudioPlayer) | ||
47 | |||
48 | iBool setInput_AVFAudioPlayer (iAVFAudioPlayer *, const iString *mediaType, const iBlock *audioFileData); | ||
49 | void play_AVFAudioPlayer (iAVFAudioPlayer *); | ||
50 | void stop_AVFAudioPlayer (iAVFAudioPlayer *); | ||
51 | void setPaused_AVFAudioPlayer (iAVFAudioPlayer *, iBool paused); | ||
52 | void setVolume_AVFAudioPlayer (iAVFAudioPlayer *, float volume); | ||
53 | |||
54 | double currentTime_AVFAudioPlayer (const iAVFAudioPlayer *); | ||
55 | double duration_AVFAudioPlayer (const iAVFAudioPlayer *); | ||
56 | iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *); | ||
57 | iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *); | ||
@@ -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 | ||
33 | static iBool isSystemDarkMode_ = iFalse; | 39 | static iBool isSystemDarkMode_ = iFalse; |
34 | static iBool isPhone_ = iFalse; | 40 | static 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 | ||
228 | static iBool isDarkMode_(iWindow *window) { | 235 | static 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 | |||
322 | enum iAVFAudioPlayerState { | ||
323 | initialized_AVFAudioPlayerState, | ||
324 | playing_AVFAudioPlayerState, | ||
325 | paused_AVFAudioPlayerState | ||
326 | }; | ||
327 | |||
328 | struct Impl_AVFAudioPlayer { | ||
329 | iString cacheFilePath; | ||
330 | AVAudioPlayer *player; | ||
331 | float volume; | ||
332 | enum iAVFAudioPlayerState state; | ||
333 | }; | ||
334 | |||
335 | iDefineTypeConstruction(AVFAudioPlayer) | ||
336 | |||
337 | void 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 | |||
344 | void deinit_AVFAudioPlayer(iAVFAudioPlayer *d) { | ||
345 | setInput_AVFAudioPlayer(d, NULL, NULL); | ||
346 | } | ||
347 | |||
348 | static const char *cacheDir_ = "~/Library/Caches/Audio"; | ||
349 | |||
350 | static 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 | |||
365 | iBool 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 | |||
394 | void play_AVFAudioPlayer(iAVFAudioPlayer *d) { | ||
395 | if (d->state != playing_AVFAudioPlayerState) { | ||
396 | [d->player play]; | ||
397 | d->state = playing_AVFAudioPlayerState; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | void stop_AVFAudioPlayer(iAVFAudioPlayer *d) { | ||
402 | [d->player stop]; | ||
403 | d->state = initialized_AVFAudioPlayerState; | ||
404 | } | ||
405 | |||
406 | void 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 | |||
417 | void setVolume_AVFAudioPlayer(iAVFAudioPlayer *d, float volume) { | ||
418 | d->volume = volume; | ||
419 | if (d->player) { | ||
420 | [d->player setVolume:volume]; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | double currentTime_AVFAudioPlayer(const iAVFAudioPlayer *d) { | ||
425 | return [d->player currentTime]; | ||
426 | } | ||
427 | |||
428 | double duration_AVFAudioPlayer(const iAVFAudioPlayer *d) { | ||
429 | return [d->player duration]; | ||
430 | } | ||
431 | |||
432 | iBool isStarted_AVFAudioPlayer(const iAVFAudioPlayer *d) { | ||
433 | return d->state != initialized_AVFAudioPlayerState; | ||
434 | } | ||
435 | |||
436 | iBool isPaused_AVFAudioPlayer(const iAVFAudioPlayer *d) { | ||
437 | return d->state == paused_AVFAudioPlayerState; | ||
438 | } | ||
diff --git a/src/media.c b/src/media.c index c3b38ae3..1313b7da 100644 --- a/src/media.c +++ b/src/media.c | |||
@@ -312,13 +312,13 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo | |||
312 | audio = at_PtrArray(&d->audio, existing - 1); | 312 | audio = at_PtrArray(&d->audio, existing - 1); |
313 | iAssert(equal_String(&audio->props.mime, mime)); /* MIME cannot change */ | 313 | iAssert(equal_String(&audio->props.mime, mime)); /* MIME cannot change */ |
314 | updateSourceData_Player(audio->player, mime, data, append_PlayerUpdate); | 314 | updateSourceData_Player(audio->player, mime, data, append_PlayerUpdate); |
315 | if (!isPartial) { | ||
316 | updateSourceData_Player(audio->player, NULL, NULL, complete_PlayerUpdate); | ||
317 | } | ||
315 | if (!isStarted_Player(audio->player)) { | 318 | if (!isStarted_Player(audio->player)) { |
316 | /* Maybe the previous updates didn't have enough data. */ | 319 | /* Maybe the previous updates didn't have enough data. */ |
317 | start_Player(audio->player); | 320 | start_Player(audio->player); |
318 | } | 321 | } |
319 | if (!isPartial) { | ||
320 | updateSourceData_Player(audio->player, NULL, NULL, complete_PlayerUpdate); | ||
321 | } | ||
322 | } | 322 | } |
323 | } | 323 | } |
324 | else if ((existing = findLinkDownload_Media(d, linkId)) != 0) { | 324 | else if ((existing = findLinkDownload_Media(d, linkId)) != 0) { |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index d847c19f..be831829 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -710,6 +710,9 @@ static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) { | |||
710 | if (document_App() != d) { | 710 | if (document_App() != d) { |
711 | return 0; | 711 | return 0; |
712 | } | 712 | } |
713 | if (get_Window()->isDrawFrozen) { | ||
714 | return 0; | ||
715 | } | ||
713 | static const uint32_t invalidInterval_ = ~0u; | 716 | static const uint32_t invalidInterval_ = ~0u; |
714 | uint32_t interval = invalidInterval_; | 717 | uint32_t interval = invalidInterval_; |
715 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 718 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
diff --git a/src/ui/window.c b/src/ui/window.c index 73590d7f..b400dced 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -1887,6 +1887,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { | |||
1887 | SDL_ShowWindow(d->win); | 1887 | SDL_ShowWindow(d->win); |
1888 | } | 1888 | } |
1889 | postRefresh_App(); | 1889 | postRefresh_App(); |
1890 | postCommand_App("media.player.update"); /* in case a player needs updating */ | ||
1890 | return iTrue; | 1891 | return iTrue; |
1891 | } | 1892 | } |
1892 | if (processEvent_Touch(&event)) { | 1893 | if (processEvent_Touch(&event)) { |