framework2

Форк
0
1226 строк · 36.3 Кб
1
//
2
//  ofAVEngineSoundPlayer.cpp
3
//  soundPlayerExample
4
//
5
//  Created by Theo Watson on 3/24/21.
6
//  Modified by Dan Rosser 9/5/22
7

8
#include "ofAVEngineSoundPlayer.h"
9

10
#ifdef OF_SOUND_PLAYER_AV_ENGINE
11

12
#include "ofUtils.h"
13
#include "ofMath.h"
14
#include "ofLog.h"
15

16
//REFS: https://github.com/ooper-shlab/AVAEMixerSample-Swift/blob/master/AVAEMixerSample/AudioEngine.m
17
// https://developer.apple.com/documentation/avfaudio/avaudioengine
18
// https://developer.apple.com/forums/thread/14138
19
// https://developer.apple.com/forums/thread/48442
20
// https://github.com/garynewby/XYAudioView/blob/master/XYAudioView/BasicAVAudioEngine.m
21
// https://github.com/twilio/video-quickstart-ios/blob/master/AudioDeviceExample/AudioDevices/ExampleAVAudioEngineDevice.m
22

23
static BOOL audioSessionSetup = NO;
24
static AVAudioEngine * _engine = nullptr;
25

26
static NSString *kShouldEnginePauseNotification = @"kShouldEnginePauseNotification";
27

28
@interface AVEnginePlayer : NSObject
29

30
@property(nonatomic, retain) NSTimer * timer;
31

32
- (BOOL)loadWithFile:(NSString*)file;
33
- (BOOL)loadWithPath:(NSString*)path;
34
- (BOOL)loadWithURL:(NSURL*)url;
35
- (BOOL)loadWithSoundFile:(AVAudioFile*)aSoundFile;
36

37
- (void)unloadSound;
38

39
- (void)play;
40
- (void)play: (float)startTime;
41
- (void)pause;
42
- (void)stop;
43

44
- (BOOL)isLoaded;
45
- (BOOL)isPlaying;
46

47
- (void)volume:(float)value;
48
- (float)volume;
49

50
- (void)pan:(float)value;
51
- (float)pan;
52

53
- (void)speed:(float)value;
54
- (float)speed;
55

56
- (void)loop:(BOOL)value;
57
- (BOOL)loop;
58

59
- (void)multiPlay:(BOOL)value;
60
- (BOOL)multiPlay;
61

62
- (void)position:(float)value;
63
- (float)position;
64

65
- (void)positionMs:(int)value;
66
- (int)positionMs;
67

68
- (float)positionSeconds;
69
- (float)soundDurationSeconds;
70

71

72
- (void)sessionInterupted;
73

74
- (AVAudioFile *)getSoundFile;
75

76
- (AVAudioEngine *)engine;
77

78
- (void)beginInterruption; /* something has caused your audio session to be interrupted */
79

80
- (void)endInterruption; /* endInterruptionWithFlags: will be called instead if implemented. */
81

82
/* notification for input become available or unavailable */
83
- (void)inputIsAvailableChanged:(BOOL)isInputAvailable;
84

85
@end
86

87

88
@interface AVEnginePlayer ()
89

90
//@property(nonatomic, strong) AVAudioEngine *engine;
91
@property(nonatomic, strong) AVAudioMixerNode *mainMixer;
92
@property(nonatomic, strong) AVAudioUnitVarispeed *variSpeed;
93
@property(nonatomic, strong) AVAudioPlayerNode *soundPlayer;
94
@property(nonatomic, strong) AVAudioFile *soundFile;
95
@property(nonatomic, assign) bool mShouldLoop;
96
@property(nonatomic, assign) BOOL bInterruptedWhileRunning;
97
@property(nonatomic, assign) bool bIsPlaying;
98
@property(nonatomic, assign) int mGaurdCount;
99
@property(nonatomic, assign) int mRestorePlayCount;
100
@property(nonatomic, assign) bool mMultiPlay;
101
@property(nonatomic, assign) bool isSessionInterrupted;
102
@property(nonatomic, assign) bool isConfigChangePending;
103
@property(nonatomic, assign) float mRequestedPositonSeconds;
104
@property(nonatomic, assign) AVAudioFramePosition startedSampleOffset;
105

106
@property(nonatomic, assign) bool mPlayingAtInterruption;
107
@property(nonatomic, assign) float mPositonSecondsAtInterruption;
108

109
@property(nonatomic, assign) BOOL resetAudioEngine;
110

111
@end
112

113
@implementation AVEnginePlayer
114

115
@synthesize timer;
116

117
- (AVAudioEngine *) engine {
118

119
  if( _engine == nullptr ){
120
    @autoreleasepool {
121
        _engine = [[AVAudioEngine alloc] init];
122
    }
123
    self.resetAudioEngine = NO;
124
  }
125
  
126
  return _engine;
127
}
128

129

130
- (void) engineReconnect {
131
    NSLog(@"engineReconnect");
132
    
133
    if( [self engine] != nil && [[self engine] isRunning] ){
134
        NSLog(@"engineReconnect isRunning");
135
    } else {
136
        NSLog(@"engineReconnect is NOT Running");
137
    }
138
    if([self engine]) {
139
        BOOL found = NO;
140
        for(AVAudioPlayerNode* node in [self engine].attachedNodes) {
141
            if(node == self.soundPlayer) {
142
                break;
143
            }
144
        }
145
        if(found) {
146
            NSLog(@"engineReconnect found Node AVAudioPlayerNode - Disconnecting");
147
            [[self engine] detachNode:self.soundPlayer];
148
        }
149
        found = NO;
150
        for(AVAudioUnitVarispeed* node in [self engine].attachedNodes) {
151
            if(node == self.variSpeed) {
152
                break;
153
            }
154
        }
155
        if(found) {
156
            NSLog(@"engineReconnect found Node  variSpeed- Disconnecting");
157
            [[self engine] detachNode:self.variSpeed];
158
        }
159
    }
160
}
161

162
- (void) engineReset {
163
    if( [self engine] != nil && [[self engine] isRunning] ){
164
        NSLog(@"engineReset isRunning");
165
    } else {
166
        NSLog(@"engineReset is NOT Running");
167
    }
168
    if([self engine] && [[self engine] isRunning]) {
169
        [_engine stop];
170
        self.resetAudioEngine = NO;
171
    }
172
    @autoreleasepool {
173
        if(_engine != nil) {
174
            _engine = nil;
175
        }
176
    }
177
    [self engine];
178
}
179

180

181
- (void)sessionInterupted {
182
    self.isSessionInterrupted = YES;
183
}
184

185

186
/* the interruption is over */
187
- (void)endInterruptionWithFlags:(NSUInteger)flags API_AVAILABLE(ios(4.0), watchos(2.0), tvos(9.0)) { /* Currently the only flag is AVAudioSessionInterruptionFlags_ShouldResume. */
188
    NSLog(@"AVEnginePlayer::endInterruptionWithFlags");
189
    if(flags == AVAudioSessionInterruptionTypeBegan) {
190
        [self beginInterruption];
191
    } else if(flags == AVAudioSessionInterruptionTypeEnded) {
192
        [self endInterruption];
193
    }
194
}
195

196
/* notification for input become available or unavailable */
197
- (void)inputIsAvailableChanged:(BOOL)isInputAvailable {
198
    NSLog(@"AVEnginePlayer::inputIsAvailableChanged");
199
}
200

201
// setupSharedSession is to prevent other iOS Classes closing the audio feed, such as AVAssetReader, when reading from disk
202
// It is set once on first launch of a AVAudioPlayer and remains as a set property from then on
203
- (void) setupSharedSession {
204
#ifndef TARGET_OSX
205
    if(audioSessionSetup) {
206
            return;
207
    }
208
    NSString * playbackCategory = AVAudioSessionCategoryPlayback;
209
	
210
    AVAudioSession * audioSession = [AVAudioSession sharedInstance];
211
    NSError * err = nil;
212
  
213
    
214
    if(![audioSession setCategory:playbackCategory
215
                                                      withOptions:AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers
216
                                    error:&err]) {
217
        
218
        NSLog(@"Unable to setCategory: withOptions error %@, %@", err, [err userInfo]);
219
        err = nil;
220
        
221
    }
222
    
223
    if(![[AVAudioSession sharedInstance] setActive: YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error: &err]) {
224
        NSLog(@"Unable to setActive: error %@, %@", err, [err userInfo]);
225
        err = nil;
226
    }
227
    
228
    double hwSampleRate = 44100.0;
229
    BOOL success = [[AVAudioSession sharedInstance] setPreferredSampleRate:hwSampleRate error:&err];
230
        if (!success) NSLog(@"Error setting preferred sample rate! %@\n", [err localizedDescription]);
231
    
232
    
233
    audioSessionSetup = YES;
234
#endif
235
}
236

237
- (instancetype)init
238
{
239
    self = [super init];
240
    if (self) {
241
		[self setupSharedSession];
242
		NSError * error = nil;
243
        
244
        _mainMixer = [[self engine] mainMixerNode];
245
		_mainMixer.outputVolume = 0.98;
246
						
247
		_mShouldLoop = false;
248
		_mGaurdCount = 0;
249
		_mMultiPlay = false;
250
        _bIsPlaying = false;
251
        _isSessionInterrupted = NO;
252
		_mRequestedPositonSeconds = 0.0f;
253
		_startedSampleOffset = 0;
254
        _bInterruptedWhileRunning = NO;
255
        _resetAudioEngine = NO;
256
        _mPlayingAtInterruption = NO;
257
        _mPositonSecondsAtInterruption = 0;
258
        _isConfigChangePending = NO;
259
		
260

261
#ifndef TARGET_OSX
262

263
		//from: https://github.com/robovm/apple-ios-samples/blob/master/UsingAVAudioEngineforPlaybackMixingandRecording/AVAEMixerSample/AudioEngine.m
264
   
265
        [[NSNotificationCenter defaultCenter] addObserver:self
266
                                                 selector:@selector(handleInterruption:)
267
                                                     name:AVAudioSessionInterruptionNotification
268
                                                   object:[AVAudioSession sharedInstance]];
269
        
270
        [[NSNotificationCenter defaultCenter] addObserver:self
271
                                                 selector:@selector(handleRouteChange:)
272
                                                     name:AVAudioSessionRouteChangeNotification
273
                                                   object:[AVAudioSession sharedInstance]];
274
        
275
        [[NSNotificationCenter defaultCenter] addObserver:self
276
                                                 selector:@selector(handleMediaServicesReset:)
277
                                                     name:AVAudioSessionMediaServicesWereLostNotification
278
                                                   object:[AVAudioSession sharedInstance]];
279
        
280
        [[NSNotificationCenter defaultCenter] addObserver:self
281
                                                 selector:@selector(handleMediaServicesReset:)
282
                                                     name:AVAudioSessionMediaServicesWereResetNotification
283
                                                   object:[AVAudioSession sharedInstance]];
284
        
285
        [[NSNotificationCenter defaultCenter] addObserver:self
286
                                                 selector:@selector(handleInterruption:)
287
                                                     name:AVAudioSessionSilenceSecondaryAudioHintNotification
288
                                                   object:[AVAudioSession sharedInstance]];
289
        
290
        
291
        
292
        if (@available(iOS 14.5, *)) {
293
            if(![[AVAudioSession sharedInstance] setPrefersNoInterruptionsFromSystemAlerts:YES error:&error]) {
294
                NSLog(@"Unable to setPrefersNoInterruptionsFromSystemAlerts: error %@, %@", error, [error userInfo]);
295
                error = nil;
296
            }
297
        } else {
298
            // Fallback on earlier versions
299
        }
300
        
301
#endif
302
        
303
        __typeof(self) __weak weak_self = self;
304

305
        [[NSNotificationCenter defaultCenter] addObserverForName:kShouldEnginePauseNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
306
                   
307
                   /* pausing stops the audio engine and the audio hardware, but does not deallocate the resources allocated by prepare().
308
                      When your app does not need to play audio, you should pause or stop the engine (as applicable), to minimize power consumption.
309
                   */
310
                    bool isPlaying = [weak_self isPlaying] || weak_self.bIsPlaying == YES;
311
                   if (!weak_self.isSessionInterrupted && !weak_self.isConfigChangePending) {
312
                       
313
                       
314
                       NSLog(@"Pausing Engine");
315
                       [[weak_self engine] pause];
316
                       [[weak_self engine] reset];
317
                       
318
                   }
319
               }];
320
		        
321
        // sign up for notifications from the engine if there's a hardware config change
322
        [[NSNotificationCenter defaultCenter] addObserverForName:AVAudioEngineConfigurationChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
323
        
324
            weak_self.resetAudioEngine = YES;
325
            
326
            NSLog(@"Received a AVAudioEngineConfigurationChangeNotification %@ notification! NOTE: %@", AVAudioEngineConfigurationChangeNotification, note.description);
327
			
328
            bool isPlaying = [weak_self isPlaying] || weak_self.bIsPlaying == YES;
329
            float posSecs = [weak_self positionSeconds];
330
            
331
            weak_self.mPlayingAtInterruption = isPlaying;
332
            weak_self.mPositonSecondsAtInterruption = posSecs;
333
            weak_self.bInterruptedWhileRunning = YES;
334
            
335
            [weak_self engineReconnect];
336
            
337
            weak_self.isConfigChangePending = YES;
338
                       
339
           if (!weak_self.isSessionInterrupted) {
340
               NSLog(@"Received a %@ notification!", AVAudioEngineConfigurationChangeNotification);
341
               NSLog(@"Re-wiring connections");
342
               [weak_self makeEngineConnections];
343
           } else {
344
               NSLog(@"Session is interrupted, deferring changes");
345
           }
346
            
347
            
348
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.033f), dispatch_get_main_queue(), ^{
349
                [weak_self handleInterruption:note];
350
            });
351
            
352
        }];
353
        
354
        
355

356
    }
357
        
358
    return self;
359
}
360

361
- (void) handleMediaServicesReset:(NSNotification *)notification {
362
    
363
    NSUInteger interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
364
    
365
        NSLog(@"Media services have been reset!");
366
       NSLog(@"Re-wiring connections and starting once again");
367
      
368
    // Re-configure the audio session per QA1749
369
       audioSessionSetup = NO;
370
       [self setupSharedSession];
371
    
372
    [self engineReset];
373
    
374
    if(_mainMixer != nil) {
375
        _mainMixer = nil;
376
    }
377
    [self engine];
378
    
379

380
    [self attachNodes];
381
    [self makeEngineConnections];
382
    
383
    
384
    [self startEngine];
385
    
386
}
387

388
- (void) handleRouteChange:(NSNotification *)notification {
389
    
390
    NSUInteger interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
391
    
392
    UInt8 reasonValue = [[notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey] intValue];
393
    
394
#ifndef TARGET_OSX
395
        AVAudioSessionRouteDescription *routeDescription = [notification.userInfo valueForKey:AVAudioSessionRouteChangePreviousRouteKey];
396
#endif
397
    
398
        NSLog(@"Route change:");
399
        switch (reasonValue) {
400
            case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
401
                NSLog(@"     NewDeviceAvailable");
402
                break;
403
            case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
404
                NSLog(@"     OldDeviceUnavailable");
405
                break;
406
            case AVAudioSessionRouteChangeReasonCategoryChange:
407
                NSLog(@"     CategoryChange");
408
#ifndef TARGET_OSX
409
                NSLog(@"     New Category: %@", [[AVAudioSession sharedInstance] category]);
410
#endif
411
                break;
412
            case AVAudioSessionRouteChangeReasonOverride:
413
                NSLog(@"     Override");
414
                break;
415
            case AVAudioSessionRouteChangeReasonWakeFromSleep:
416
                NSLog(@"     WakeFromSleep");
417
                break;
418
            case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
419
                NSLog(@"     NoSuitableReasonForCategory");
420
                break;
421
            default:
422
                NSLog(@"     ReasonUnknown");
423
        }
424
    
425
#ifndef TARGET_OSX
426
        NSLog(@"Previous route:\n");
427
        NSLog(@"%@", routeDescription);
428
#endif
429
}
430

431
- (void) handleInterruption:(NSNotification *)notification {
432
    
433
    NSUInteger interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
434

435
    
436
    NSLog(@"AVEnginePlayer::handleInterruption: notification:%@ %@ interruptionType: %lu", notification.name, notification.description, (unsigned long)interruptionType);
437
       
438

439
    if(interruptionType == AVAudioSessionInterruptionTypeBegan) {
440
        [self beginInterruption];
441
    } else if(interruptionType == AVAudioSessionInterruptionTypeEnded) {
442
        [self endInterruption];
443
        
444
        [self startEngine];
445
    }
446

447
}
448

449
- (void)beginInterruption {
450
    
451
    NSLog(@"AVEnginePlayer::beginInterruption");
452
    
453
    _isSessionInterrupted = YES;
454
    
455
    if([self isPlaying] || _bIsPlaying == YES) {
456
        self.bInterruptedWhileRunning = YES;
457
    }
458
    
459
    if([self isValid]) {
460
        [self.soundPlayer stop];
461
    }
462
    
463
//    if([self.delegate respondsToSelector:@selector(soundStreamBeginInterruption:)]) {
464
//        [self.delegate soundStreamBeginInterruption:self];
465
//    }
466
}
467

468

469
- (void) attachNodes {
470
    if( self.variSpeed == nullptr ){
471
        // Speed manipulator
472
        @autoreleasepool {
473
            self.variSpeed = [[AVAudioUnitVarispeed alloc] init];
474
        }
475
        self.variSpeed.rate = 1.0;
476

477
    }
478
    
479
    if( self.soundPlayer == nil ){
480
        // Sound player
481
        @autoreleasepool {
482
            self.soundPlayer = [[AVAudioPlayerNode alloc] init];
483
        }
484
    }
485
    
486
    if(self.soundPlayer != nil && self.variSpeed != nil) {
487
        [[self engine] attachNode:self.soundPlayer];
488
        [[self engine] attachNode:self.variSpeed];
489
    }
490
}
491

492
- (void) makeEngineConnections {
493
    _mainMixer = [[self engine] mainMixerNode];
494
    
495
    AVAudioFormat * stereoFormat;
496
    @autoreleasepool {
497
       stereoFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100 channels:2];
498
    }
499
    if(self.soundPlayer != nil) {
500
        [[self engine] connect:self.soundPlayer to:self.variSpeed format:stereoFormat];
501
    }
502
    if(self.variSpeed != nil) {
503
        [[self engine] connect:self.variSpeed to:self.mainMixer format:stereoFormat];
504
    }
505
}
506

507
- (void)endInterruption {
508
    NSLog(@"AVEnginePlayer::endInterruption");
509
    
510
    NSError *error;
511
#ifndef TARGET_OSX
512
    bool success = [[AVAudioSession sharedInstance] setActive:YES error:&error];
513
    if (!success)
514
        NSLog(@"AVAudioSession set active failed with error: %@", [error localizedDescription]);
515
    else {
516
#endif
517
        _isSessionInterrupted = NO;
518
        if (_isConfigChangePending) {
519
            // there is a pending config changed notification
520
            NSLog(@"Responding to earlier engine config changed notification. Re-wiring connections");
521
            [self startEngine];
522
            [self makeEngineConnections];
523
            
524
            _isConfigChangePending = NO;
525
        }
526
#ifndef TARGET_OSX
527
    }
528
#endif
529
//    [self engineReconnect];
530
    
531

532
    
533
    
534
    if(self.bInterruptedWhileRunning || _bIsPlaying == YES) {
535
        self.bInterruptedWhileRunning = NO;
536
        bool isPlaying = [self isPlaying] || _bIsPlaying == YES;
537
        float posSecs = [self positionSeconds] > 0 ? [self positionSeconds] : _mPositonSecondsAtInterruption;
538
        
539
//                std::cout << " isPlaying is " << isPlaying << " pos secs is " << posSecs << std::endl;
540
        
541
        
542
        if( isPlaying && posSecs >= 0 && posSecs < ([self soundDurationSeconds] + 0.017f) && self.mRestorePlayCount == 0){
543
            self.mRestorePlayCount++;
544
            
545
        __typeof(self) __weak weak_self = self;
546
    
547
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.017f), dispatch_get_main_queue(), ^{
548
                weak_self.mRestorePlayCount--;
549
                if(weak_self.bIsPlaying == NO) return;
550
                [weak_self play:posSecs];
551
            });
552
        }
553
    }
554
    
555
//    if([self.delegate respondsToSelector:@selector(soundStreamEndInterruption:)]) {
556
//        [self.delegate soundStreamEndInterruption:self];
557
//    }
558
}
559

560
//----------------------------------------------------------- load / unload.
561
- (BOOL)loadWithFile:(NSString*)file {
562
    NSArray * fileSplit = [file componentsSeparatedByString:@"."];
563
    NSURL * fileURL = [[NSBundle mainBundle] URLForResource:[fileSplit objectAtIndex:0]
564
                                              withExtension:[fileSplit objectAtIndex:1]];
565
	return [self loadWithURL:fileURL];
566
}
567

568
- (BOOL)loadWithPath:(NSString*)path {
569
    NSURL * fileURL = [NSURL fileURLWithPath:path];
570
	return [self loadWithURL:fileURL];
571
}
572

573
- (BOOL)loadWithURL:(NSURL*)url {
574
    [self stop];
575

576
	NSError *error;
577
    @autoreleasepool {
578
        self.soundFile = [[AVAudioFile alloc] initForReading:url error:&error];
579
    }
580
    if (error) {
581
        NSLog(@"Error: %@", [error localizedDescription]);
582
        self.soundFile = nil;
583
		return NO;
584
    }else{
585
		NSLog(@"Sound file %@ loaded!", url);
586
	}
587
	
588
	return [self loadWithSoundFile:self.soundFile];
589
}
590

591
- (void)startEngine{
592
    
593
    NSError * error = nil;
594
    BOOL engineRunning = NO;
595
    BOOL problem = NO;
596
    if( ![[self engine] isRunning] ){
597
        [[self engine] startAndReturnError:&error];
598
        if (error) {
599
            NSLog(@"Error starting engine: %@", [error localizedDescription]);
600
//            if(self.resetAudioEngine) {
601
//                [self engineReset];
602
//            }
603
            problem = YES;
604
            
605
        } else {
606
            NSLog(@"Engine start successful");
607
            if(self.resetAudioEngine) {
608
//                [self engineReset];
609
                if(self.resetAudioEngine == NO)
610
                    problem = YES;
611
                
612
            }
613
            engineRunning = YES;
614
//            [self engineReconnect];
615
        }
616
    }else{
617
//        NSLog(@"Engine already running");
618
        engineRunning = YES;
619
    }
620
    
621
    if( self.variSpeed == nullptr ){
622
        // Speed manipulator
623
        @autoreleasepool {
624
            self.variSpeed = [[AVAudioUnitVarispeed alloc] init];
625
        }
626
        self.variSpeed.rate = 1.0;
627
        problem = YES;
628
    }
629
    
630
    if( self.soundPlayer == nil ){
631
        // Sound player
632
        @autoreleasepool {
633
            self.soundPlayer = [[AVAudioPlayerNode alloc] init];
634
        }
635
        problem = YES;
636
    }
637

638
    if(_mainMixer != [[self engine] mainMixerNode]) {
639
        _mainMixer = [[self engine] mainMixerNode];
640
        problem = YES;
641
    }
642
    
643
    if(problem == YES) {
644
        //[[self engine] reset];
645
        //NSLog(@"Engine start successful - re-attaching nodes");
646
        [[self engine] attachNode:self.variSpeed];
647
        [[self engine] attachNode:self.soundPlayer];
648
        
649
        [[self engine]  connect:self.variSpeed to:self.mainMixer format:[_mainMixer outputFormatForBus:0]];
650
        [[self engine]  connect:self.soundPlayer to:self.variSpeed format:[_mainMixer outputFormatForBus:0]];
651
    }
652
}
653

654
- (BOOL)loadWithSoundFile:(AVAudioFile *)aSoundFile {
655
    [self stop];
656

657
    self.soundFile = aSoundFile;
658
	
659
	[self startEngine];
660
	
661
	self.mGaurdCount=0;
662
	self.mRequestedPositonSeconds = 0;
663
	self.startedSampleOffset = 0;
664
	self.mRestorePlayCount =0;
665
	
666
    return YES;
667
}
668

669
- (void)dealloc {
670
    [self unloadSound];
671
}
672

673
- (BOOL)isValid {
674
    if(self.soundPlayer != nil && self.soundPlayer.engine != nil) {
675
        return YES;
676
    }
677
    return NO;
678
}
679

680
- (void)unloadSound {
681
    [self stop];
682
    if([self isValid]) {
683
        [[self engine] detachNode:self.soundPlayer];
684
    }
685
    self.soundPlayer = nil;
686
    self.soundFile = nil;
687
    self.variSpeed = nil;
688
}
689

690
- (void)play{
691
    [self stop];
692
    self.mRequestedPositonSeconds = 0.0;
693
    [self play:self.mRequestedPositonSeconds];
694
}
695

696
- (void)playloop{
697
    self.mRequestedPositonSeconds = 0.0;
698
    [self play:self.mRequestedPositonSeconds];
699
}
700

701
//----------------------------------------------------------- play / pause / stop.
702
- (void)play:(float)startTime{
703
    if([self isPlaying]) {
704
        [self pause];
705
    }
706
    if(self.soundPlayer == nil) {
707
        NSLog(@"play - soundPlayer is null");
708
        return;
709
    }
710
    if(_isSessionInterrupted || _isConfigChangePending){
711
        __typeof(self) __weak weak_self = self;
712
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3f), dispatch_get_main_queue(), ^{
713
            [weak_self play:startTime];
714
        });
715
        return;
716
    }
717
    
718
    if( self.engine == nil || ![[self engine] isRunning] ){
719
        NSLog(@"play - engine is null or not running - starting");
720
        [self startEngine];
721
    }
722
    
723
    if( self.engine == nil || ![[self engine] isRunning] ){
724
        NSLog(@"play - engine is NULL or not running");
725
        return;
726
    }
727
    BOOL found = NO;
728
    
729
    for(AVAudioPlayerNode* node in [self engine].attachedNodes) {
730
        if(node == self.soundPlayer) {
731
            found = YES;
732
            break;
733
        }
734
    }
735
    
736
    if(self.soundPlayer.engine == nil || found == NO) {
737
        
738
        if(found == NO) {
739
            NSLog(@"play - engine is valid - however NO attachedNode for soundPlayer found - reseting");
740
        } else {
741
            NSLog(@"play - engine is valid - however soundPlayer.engine is NULL - reseting");
742
        }
743
        _mainMixer = [[self engine] mainMixerNode];
744
        
745
        [[self engine] attachNode:self.soundPlayer];
746
        [[self engine] connect:self.soundPlayer to:self.variSpeed format:[_mainMixer outputFormatForBus:0]];
747
        
748
        [[self engine] attachNode:self.variSpeed];
749
        [[self engine] connect:self.variSpeed to:self.mainMixer format:[_mainMixer outputFormatForBus:0]];
750
    }
751
    
752
    //we do this as we can't cancel completion handlers.
753
    //and queded completion handlers can interupt a playing track if we have retriggered it
754
    //so we basically do this to execute only the last completion handler ( the one for the current playing track ), and ignore the earlier ones.
755
	self.mGaurdCount++;
756
    _mPositonSecondsAtInterruption = 0;
757
    _isSessionInterrupted = NO;
758
    _mPlayingAtInterruption = NO;
759
    self.mRestorePlayCount = 0;
760
	
761
	self.startedSampleOffset = self.soundFile.processingFormat.sampleRate * startTime;
762
	AVAudioFramePosition numFramesToPlay = self.soundFile.length - self.startedSampleOffset;
763
    const float epsilon = 0.0000001f;
764
	if( startTime <= epsilon){
765
		self.startedSampleOffset = 0;
766
		numFramesToPlay = self.soundFile.length;
767
	}
768
	
769
//	std::cout << " startedSampleOffset is " << self.startedSampleOffset <<  " numFrames is " << numFramesToPlay << " self.mGaurdCount is " << self.mGaurdCount << std::endl;
770
	
771
	self.mRestorePlayCount = 0;
772
    
773
   
774
	
775
    [self.soundPlayer play];
776
    _bIsPlaying = YES;
777
    
778
        __typeof(self) __weak weak_self = self;
779

780
	[self.soundPlayer scheduleSegment:self.soundFile startingFrame:self.startedSampleOffset frameCount:numFramesToPlay atTime:0 completionHandler:^{
781
	
782
		dispatch_async(dispatch_get_main_queue(), ^{
783
			weak_self.mGaurdCount--;
784
			
785
            if(weak_self.bIsPlaying == NO) return;
786
            
787
			//std::cout << " need gaurd is " << self.mGaurdCount << std::endl;
788

789
			if( weak_self.mGaurdCount == 0 ){
790
				float time = [weak_self positionSeconds];
791
				
792
				float duration = [weak_self soundDurationSeconds];
793
				
794
				//have to do all this because the completion handler fires before the player is actually finished - which isn't very helpful
795
				float remainingTime = duration-time;
796
				if( remainingTime < 0 ){
797
					remainingTime = 0;
798
				}
799

800
				//all the other time stuff accounts for the speed / rate, except the remaining time delay
801
				remainingTime /= ofClamp(self.variSpeed.rate, 0.01, 100.0);
802
                
803
				if( weak_self.mShouldLoop ){
804
                    [NSObject cancelPreviousPerformRequestsWithTarget:weak_self selector:@selector(playloop) object: weak_self.soundPlayer];
805
					[weak_self performSelector:@selector(playloop) withObject:self.soundPlayer afterDelay:remainingTime];
806
				}else{
807
                    [NSObject cancelPreviousPerformRequestsWithTarget:weak_self selector:@selector(stop) object: weak_self.soundPlayer];
808
					[weak_self performSelector:@selector(stop) withObject:weak_self.soundPlayer afterDelay:remainingTime];
809
				}
810
                
811
//                NSLog(@"play - scheduleSegment mShouldLoop:%i - mGaurdCount:%i", _mShouldLoop, _mGaurdCount);
812
            } else {
813
//                NSLog(@"play - scheduleSegment - mGaurdCount:%i", _mGaurdCount);
814
            }
815
			
816
			if( weak_self.mGaurdCount < 0 ){
817
				weak_self.mGaurdCount=0;
818
			}
819
			
820
		});
821
		
822
	}];
823
}
824

825
- (AVAudioFile *)getSoundFile{
826
	return self.soundFile;
827
}
828

829
- (void)pause {
830
    _bIsPlaying = NO;
831
    if([self isValid]) {
832
        [self.soundPlayer pause];
833
    }
834
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(playloop) object: self.soundPlayer];
835
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stop) object: self.soundPlayer];
836
}
837

838
- (void)stop {
839
    
840
    __typeof(self) __weak weak_self = self;
841

842
    if(_isSessionInterrupted || _isConfigChangePending){
843
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3f), dispatch_get_main_queue(), ^{
844
            [weak_self stop];
845
        });
846
        return;
847
    }
848
    
849
    if([self isValid]) {
850
        [self.soundPlayer stop];
851
    }
852
    _bIsPlaying = NO;
853
   
854
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(playloop) object: self.soundPlayer];
855
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(stop) object: self.soundPlayer];
856

857
    self.startedSampleOffset = 0;
858
}
859

860
//----------------------------------------------------------- states.
861
- (BOOL)isLoaded {
862
    return (self.soundPlayer != nil);
863
}
864

865
- (BOOL)isPlaying {
866
    if(![self isValid]) return NO;
867
    return self.soundPlayer.isPlaying;
868
}
869

870
//----------------------------------------------------------- properties.
871
- (void)volume:(float)value {
872
    if(![self isValid]) return;
873
    self.soundPlayer.volume = value;
874
}
875

876
- (float)volume {
877
    if(![self isValid]) return 0;
878
    return self.soundPlayer.volume;
879
}
880

881
- (void)pan:(float)value {
882
    if(![self isValid]) return;
883
    self.soundPlayer.pan = value;
884
}
885

886
- (float)pan {
887
    if(![self isValid]) return 0;
888
    return self.soundPlayer.pan;
889
}
890

891
- (void)speed:(float)value {
892
    if([self isValid]) {
893
		self.variSpeed.rate = value;
894
    }
895
}
896

897
- (float)speed {
898
    if(![self isValid]) return 0;
899
    return self.variSpeed.rate;
900
}
901

902
- (void)loop:(BOOL)bLoop {
903
    self.mShouldLoop = bLoop;
904
}
905

906
- (BOOL)loop {
907
    return self.mShouldLoop;
908
}
909

910
- (void)multiPlay:(BOOL)value {
911
    self.mMultiPlay = value;
912
}
913

914
- (BOOL)multiPlay {
915
    return self.mMultiPlay;
916
}
917

918
- (void)position:(float)value {
919
	if( [self isLoaded] ){
920
		self.mRequestedPositonSeconds = ofClamp(value, 0, 1) * [self soundDurationSeconds];
921
		
922
		if( [self isPlaying] ){
923
			[self play];
924
		}
925
	}
926
}
927

928
- (float)position {
929
    if(self.soundPlayer == nil) {
930
        return 0;
931
    }
932
    
933
    if( [self isPlaying] ){
934
		float time = [self positionSeconds];
935
		float duration = [self soundDurationSeconds];
936
		
937
		if( duration > 0 ){
938
			float pct = ofClamp(time/duration, 0, 1);
939
			//NSLog(@"time is %f out of %f pct is %f", time, duration, pct );
940
			return pct;
941
		}
942
	}
943
	return 0;
944
}
945

946
- (void)positionMs:(int)value {
947
	if( [self isLoaded] ){
948
        float oldDuration = [self positionSeconds];
949
		self.mRequestedPositonSeconds = ofClamp(((float)value)/1000.0, 0, [self soundDurationSeconds]);
950
        
951
        NSLog(@"positionMS: from: %f toNewPos: %f", oldDuration, self.mRequestedPositonSeconds );
952
		
953
		if( [self isPlaying] ){
954
			[self play];
955
		}
956
	}
957
}
958

959
- (int)positionMs {
960
	float timeSeconds = [self positionSeconds];
961
	return timeSeconds/1000.0;
962
}
963

964
- (float)positionSeconds{
965
	if( [self isPlaying] && self.variSpeed != nil && self.engine != nil){
966
		AVAudioTime * playerTime =  [self.soundPlayer playerTimeForNodeTime:self.soundPlayer.lastRenderTime];
967
        float time = 0;
968
        if(playerTime != nil)
969
            time =((self.startedSampleOffset + playerTime.sampleTime) / playerTime.sampleRate);
970
		return time;
971
	}
972
	return 0.0;
973
}
974

975
- (float)soundDurationSeconds{
976
	if( [self isLoaded] && self.variSpeed != nil && self.engine != nil){
977
        float duration = 0.0;
978
        if(self.soundFile.processingFormat != nil)
979
            duration = self.soundFile.length / self.soundFile.processingFormat.sampleRate;
980
		return duration;
981
	}
982
	return 0.0;
983
}
984

985
@end
986

987

988

989
using namespace std;
990

991
ofAVEngineSoundPlayer::ofAVEngineSoundPlayer() {
992
    soundPlayer = NULL;
993
}
994

995
ofAVEngineSoundPlayer::~ofAVEngineSoundPlayer() {
996
    unload();
997
}
998

999
bool ofAVEngineSoundPlayer::load(const std::filesystem::path& fileName, bool stream) {
1000
    if(soundPlayer != NULL) {
1001
        unload();
1002
    }
1003

1004
    string filePath = ofToDataPath(fileName);
1005
    @autoreleasepool {
1006
        soundPlayer = [[AVEnginePlayer alloc] init];
1007
    }
1008
    BOOL bOk = [(AVEnginePlayer *)soundPlayer loadWithPath:[NSString stringWithUTF8String:filePath.c_str()]];
1009
    
1010
    return bOk;
1011
}
1012

1013
void ofAVEngineSoundPlayer::unload() {
1014
    if(soundPlayer != NULL) {
1015
        
1016
        [(AVEnginePlayer *)soundPlayer unloadSound];
1017
        @autoreleasepool {
1018
            soundPlayer = nil;
1019
        }
1020
    }
1021
    if( bAddedUpdate ){
1022
        ofRemoveListener(ofEvents().update, this, &ofAVEngineSoundPlayer::updateFunction);
1023
        bAddedUpdate = false;
1024
    }
1025
    cleanupMultiplayers();
1026
}
1027

1028
void ofAVEngineSoundPlayer::play() {
1029
    if(soundPlayer == NULL) {
1030
        return;
1031
    }
1032
    
1033
    auto mainPlayer = (AVEnginePlayer *)soundPlayer;
1034
    if( [mainPlayer multiPlay] && [mainPlayer isPlaying] ){
1035
            
1036
        AVEnginePlayer * extraPlayer;
1037
        @autoreleasepool {
1038
           extraPlayer = [[AVEnginePlayer alloc] init];
1039
        }
1040
        BOOL bOk = [extraPlayer loadWithSoundFile:[mainPlayer getSoundFile]];
1041
        if( bOk ){
1042
                [extraPlayer speed:[mainPlayer speed]];
1043
                [extraPlayer pan:[mainPlayer pan]];
1044
                [extraPlayer volume:[mainPlayer volume]];
1045
                [extraPlayer play];
1046
                
1047
                mMultiplayerSoundPlayers.push_back(extraPlayer);
1048
                
1049
                if( !bAddedUpdate ){
1050
                    ofAddListener(ofEvents().update, this, &ofAVEngineSoundPlayer::updateFunction);
1051
                    bAddedUpdate = true;
1052
                }
1053
        }
1054
            
1055
    }else{
1056
        [(AVEnginePlayer *)soundPlayer play];
1057
    }
1058
}
1059

1060
void ofAVEngineSoundPlayer::cleanupMultiplayers(){
1061
	for( auto mMultiPlayer : mMultiplayerSoundPlayers ){
1062
		if( mMultiPlayer != NULL ){
1063
			[(AVEnginePlayer *)mMultiPlayer stop];
1064
			[(AVEnginePlayer *)mMultiPlayer unloadSound];
1065
                        @autoreleasepool {
1066
                            mMultiPlayer = nil;
1067
                        }
1068
		}
1069
	}
1070
	mMultiplayerSoundPlayers.clear();
1071
}
1072

1073
bool ofAVEngineSoundPlayer::removeMultiPlayer(void * aPlayer){
1074
	return( aPlayer == NULL );
1075
}
1076

1077
//better do do this in a thread?
1078
//feels safer to use ofEvents().update so we don't need to lock.
1079
void ofAVEngineSoundPlayer::updateFunction( ofEventArgs & args ){
1080
		
1081
	vector <ObjectType> playerPlayingList;
1082

1083
	for( auto mMultiPlayerPtr : mMultiplayerSoundPlayers ){
1084
            if( mMultiPlayerPtr != NULL ){
1085
                if( [(AVEnginePlayer *)mMultiPlayerPtr isLoaded] && [(AVEnginePlayer *)mMultiPlayerPtr isPlaying] ){
1086
                    playerPlayingList.push_back(mMultiPlayerPtr);
1087
                }else{
1088
                    [(AVEnginePlayer *)mMultiPlayerPtr unloadSound];
1089
                    @autoreleasepool {
1090
                        mMultiPlayerPtr = nil;
1091
                    }
1092
                }
1093
            }
1094
	}
1095
	
1096
	mMultiplayerSoundPlayers = playerPlayingList;
1097
}
1098

1099
void ofAVEngineSoundPlayer::stop() {
1100
    if(soundPlayer == NULL) {
1101
        return;
1102
    }
1103
    [(AVEnginePlayer *)soundPlayer stop];
1104
	cleanupMultiplayers();
1105
}
1106

1107
void ofAVEngineSoundPlayer::setVolume(float value) {
1108
    if(soundPlayer == NULL) {
1109
        return;
1110
    }
1111
    [(AVEnginePlayer *)soundPlayer volume:value];
1112
}
1113

1114
void ofAVEngineSoundPlayer::setPan(float value) {
1115
    if(soundPlayer == NULL) {
1116
        return;
1117
    }
1118
    [(AVEnginePlayer *)soundPlayer pan:value];
1119
}
1120

1121
void ofAVEngineSoundPlayer::setSpeed(float value) {
1122
    if(soundPlayer == NULL) {
1123
        return;
1124
    }
1125
    [(AVEnginePlayer *)soundPlayer speed:value];
1126
}
1127

1128
void ofAVEngineSoundPlayer::setPaused(bool bPause) {
1129
    if(soundPlayer == NULL) {
1130
        return;
1131
    }
1132
    if(bPause) {
1133
        [(AVEnginePlayer *)soundPlayer pause];
1134
    } else {
1135
        [(AVEnginePlayer *)soundPlayer play];
1136
    }
1137
}
1138

1139
void ofAVEngineSoundPlayer::setLoop(bool bLoop) {
1140
    if(soundPlayer == NULL) {
1141
        return;
1142
    }
1143
    [(AVEnginePlayer *)soundPlayer loop:bLoop];
1144
}
1145

1146
void ofAVEngineSoundPlayer::setMultiPlay(bool bMultiPlay) {
1147
    if(soundPlayer == NULL) {
1148
        return;
1149
    }
1150
    [(AVEnginePlayer *)soundPlayer multiPlay:bMultiPlay];
1151
}
1152

1153
void ofAVEngineSoundPlayer::setPosition(float position) {
1154
    if(soundPlayer == NULL) {
1155
        return;
1156
    }
1157
    [(AVEnginePlayer *)soundPlayer position:position];
1158
}
1159

1160
void ofAVEngineSoundPlayer::setPositionMS(int positionMS) {
1161
    if(soundPlayer == NULL) {
1162
        return;
1163
    }
1164
    [(AVEnginePlayer *)soundPlayer positionMs:positionMS];
1165
}
1166

1167
float ofAVEngineSoundPlayer::getPosition()  const{
1168
    if(soundPlayer == NULL) {
1169
        return 0;
1170
    }
1171
    return [(AVEnginePlayer *)soundPlayer position];
1172
}
1173

1174
int ofAVEngineSoundPlayer::getPositionMS() const {
1175
    if(soundPlayer == NULL) {
1176
        return 0;
1177
    }
1178
    return [(AVEnginePlayer *)soundPlayer positionMs];
1179
}
1180

1181
bool ofAVEngineSoundPlayer::isPlaying()  const{
1182
    if(soundPlayer == NULL) {
1183
        return false;
1184
    }
1185
    
1186
    bool bMainPlaying = [(AVEnginePlayer *)soundPlayer isPlaying];
1187
    if( !bMainPlaying && mMultiplayerSoundPlayers.size() ){
1188
		return true;
1189
	}
1190
	
1191
	return bMainPlaying;
1192
}
1193

1194
float ofAVEngineSoundPlayer::getSpeed()  const{
1195
    if(soundPlayer == NULL) {
1196
        return 0;
1197
    }
1198
    return [(AVEnginePlayer *)soundPlayer speed];
1199
}
1200

1201
float ofAVEngineSoundPlayer::getPan()  const{
1202
    if(soundPlayer == NULL) {
1203
        return 0;
1204
    }
1205
    return [(AVEnginePlayer *)soundPlayer pan];
1206
}
1207

1208
bool ofAVEngineSoundPlayer::isLoaded()  const{
1209
    if(soundPlayer == NULL) {
1210
        return false;
1211
    }
1212
    return [(AVEnginePlayer *)soundPlayer isLoaded];
1213
}
1214

1215
float ofAVEngineSoundPlayer::getVolume()  const{
1216
    if(soundPlayer == NULL) {
1217
        return false;
1218
    }
1219
    return [(AVEnginePlayer *)soundPlayer volume];
1220
}
1221

1222
void * ofAVEngineSoundPlayer::getAVEnginePlayer() {
1223
    return (__bridge void *)soundPlayer;
1224
}
1225

1226
#endif
1227

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.