/libs/ObjectAL/OpenAL/ALSource.m
Objective C | 1315 lines | 1072 code | 190 blank | 53 comment | 104 complexity | 117557c0304ae421211838b55dc30765 MD5 | raw file
Possible License(s): Apache-2.0
1// 2// ALSource.m 3// ObjectAL 4// 5// Created by Karl Stenerud on 15/12/09. 6// 7// Copyright 2009 Karl Stenerud 8// 9// Licensed under the Apache License, Version 2.0 (the "License"); 10// you may not use this file except in compliance with the License. 11// You may obtain a copy of the License at 12// 13// http://www.apache.org/licenses/LICENSE-2.0 14// 15// Unless required by applicable law or agreed to in writing, software 16// distributed under the License is distributed on an "AS IS" BASIS, 17// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18// See the License for the specific language governing permissions and 19// limitations under the License. 20// 21// Note: You are NOT required to make the license available from within your 22// iOS application. Including it in your project is sufficient. 23// 24// Attribution is not required, but appreciated :) 25// 26 27#import "ALSource.h" 28#import "ObjectALMacros.h" 29#import "ALWrapper.h" 30#import "OpenALManager.h" 31#import "OALAudioActions.h" 32#import "OALUtilityActions.h" 33 34 35#pragma mark - 36#pragma mark Private Methods 37 38/** 39 * (INTERNAL USE) Private methods for ALSource. 40 */ 41@interface ALSource (Private) 42 43/** (INTERNAL USE) Close any resources belonging to the OS. 44 */ 45- (void) closeOSResources; 46 47/** (INTERNAL USE) Called by SuspendHandler. 48 */ 49- (void) setSuspended:(bool) value; 50 51/** (INTERNAL USE) Callback for resuming playback after delay to 52 * get around OpenAL bug. 53 */ 54- (void) delayedResumePlayback; 55 56 57@end 58 59 60@implementation ALSource 61 62#pragma mark Object Management 63 64+ (id) source 65{ 66 return [[[self alloc] init] autorelease]; 67} 68 69+ (id) sourceOnContext:(ALContext*) context 70{ 71 return [[[self alloc] initOnContext:context] autorelease]; 72} 73 74- (id) init 75{ 76 return [self initOnContext:[OpenALManager sharedInstance].currentContext]; 77} 78 79- (id) initOnContext:(ALContext*) contextIn 80{ 81 if(nil != (self = [super init])) 82 { 83 OAL_LOG_DEBUG(@"%@: Init on context %@", self, contextIn); 84 85 if(nil == contextIn) 86 { 87 OAL_LOG_ERROR(@"%@: Failed to init because context was nil. Returning nil", self); 88 [self release]; 89 return nil; 90 } 91 92 suspendHandler = [[OALSuspendHandler alloc] initWithTarget:self selector:@selector(setSuspended:)]; 93 94 context = [contextIn retain]; 95 @synchronized([OpenALManager sharedInstance]) 96 { 97 ALContext* realContext = [OpenALManager sharedInstance].currentContext; 98 [OpenALManager sharedInstance].currentContext = context; 99 sourceId = [ALWrapper genSource]; 100 [OpenALManager sharedInstance].currentContext = realContext; 101 } 102 OAL_LOG_DEBUG(@"%@: Created source %08x", self, sourceId); 103 104 [context notifySourceInitializing:self]; 105 gain = [ALWrapper getSourcef:sourceId parameter:AL_GAIN]; 106 shadowState = AL_INITIAL; 107 108 [context addSuspendListener:self]; 109 } 110 return self; 111} 112 113- (void) dealloc 114{ 115 OAL_LOG_DEBUG(@"%@: Dealloc, sourceId = %08x", self, sourceId); 116 117 [context removeSuspendListener:self]; 118 [context notifySourceDeallocating:self]; 119 120 [self closeOSResources]; 121 122 [gainAction stopAction]; 123 [gainAction release]; 124 [panAction stopAction]; 125 [panAction release]; 126 [pitchAction stopAction]; 127 [pitchAction release]; 128 [suspendHandler release]; 129 [context release]; 130 131 // In IOS 3.x, OpenAL doesn't stop playing right away. 132 // Release after a delay to give it some time to stop. 133 [buffer performSelector:@selector(release) withObject:nil afterDelay:0.1]; 134 135 [super dealloc]; 136} 137 138- (void) closeOSResources 139{ 140 OPTIONALLY_SYNCHRONIZED(self) 141 { 142 if((ALuint)AL_INVALID != sourceId) 143 { 144 [ALWrapper sourceStop:sourceId]; 145 [ALWrapper sourcei:sourceId parameter:AL_BUFFER value:AL_NONE]; 146 147 @synchronized([OpenALManager sharedInstance]) 148 { 149 ALContext* realContext = [OpenALManager sharedInstance].currentContext; 150 if(realContext != context) 151 { 152 // Make this source's context the current one if it isn't already. 153 [OpenALManager sharedInstance].currentContext = context; 154 } 155 156 [ALWrapper deleteSource:sourceId]; 157 158 [OpenALManager sharedInstance].currentContext = realContext; 159 } 160 sourceId = (ALuint)AL_INVALID; 161 } 162 } 163} 164 165- (void) close 166{ 167 [self closeOSResources]; 168} 169 170 171#pragma mark Properties 172 173- (ALBuffer*) buffer 174{ 175 OPTIONALLY_SYNCHRONIZED(self) 176 { 177 return buffer; 178 } 179} 180 181- (void) setBuffer:(ALBuffer *) value 182{ 183 OPTIONALLY_SYNCHRONIZED(self) 184 { 185 if(self.suspended) 186 { 187 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 188 return; 189 } 190 191 [self stop]; 192 193 // In IOS 3.x, OpenAL doesn't stop playing right away. 194 // Release after a delay to give it some time to stop. 195 [buffer performSelector:@selector(release) withObject:nil afterDelay:0.1]; 196 197 buffer = [value retain]; 198 [ALWrapper sourcei:sourceId parameter:AL_BUFFER value:buffer.bufferId]; 199 } 200} 201 202- (int) buffersQueued 203{ 204 return [ALWrapper getSourcei:sourceId parameter:AL_BUFFERS_QUEUED]; 205} 206 207- (int) buffersProcessed 208{ 209 return [ALWrapper getSourcei:sourceId parameter:AL_BUFFERS_PROCESSED]; 210} 211 212- (float) coneInnerAngle 213{ 214 OPTIONALLY_SYNCHRONIZED(self) 215 { 216 return [ALWrapper getSourcef:sourceId parameter:AL_CONE_INNER_ANGLE]; 217 } 218} 219 220- (void) setConeInnerAngle:(float) value 221{ 222 OPTIONALLY_SYNCHRONIZED(self) 223 { 224 if(self.suspended) 225 { 226 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 227 return; 228 } 229 230 [ALWrapper sourcef:sourceId parameter:AL_CONE_INNER_ANGLE value:value]; 231 } 232} 233 234- (float) coneOuterAngle 235{ 236 OPTIONALLY_SYNCHRONIZED(self) 237 { 238 return [ALWrapper getSourcef:sourceId parameter:AL_CONE_OUTER_ANGLE]; 239 } 240} 241 242- (void) setConeOuterAngle:(float) value 243{ 244 OPTIONALLY_SYNCHRONIZED(self) 245 { 246 if(self.suspended) 247 { 248 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 249 return; 250 } 251 252 [ALWrapper sourcef:sourceId parameter:AL_CONE_OUTER_ANGLE value:value]; 253 } 254} 255 256- (float) coneOuterGain 257{ 258 OPTIONALLY_SYNCHRONIZED(self) 259 { 260 return [ALWrapper getSourcef:sourceId parameter:AL_CONE_OUTER_GAIN]; 261 } 262} 263 264- (void) setConeOuterGain:(float) value 265{ 266 OPTIONALLY_SYNCHRONIZED(self) 267 { 268 if(self.suspended) 269 { 270 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 271 return; 272 } 273 274 [ALWrapper sourcef:sourceId parameter:AL_CONE_OUTER_GAIN value:value]; 275 } 276} 277 278@synthesize context; 279 280- (ALVector) direction 281{ 282 ALVector result; 283 OPTIONALLY_SYNCHRONIZED(self) 284 { 285 [ALWrapper getSource3f:sourceId parameter:AL_DIRECTION v1:&result.x v2:&result.y v3:&result.z]; 286 } 287 return result; 288} 289 290- (void) setDirection:(ALVector) value 291{ 292 OPTIONALLY_SYNCHRONIZED(self) 293 { 294 if(self.suspended) 295 { 296 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 297 return; 298 } 299 300 [ALWrapper source3f:sourceId parameter:AL_DIRECTION v1:value.x v2:value.y v3:value.z]; 301 } 302} 303 304- (float) volume 305{ 306 return self.gain; 307} 308 309- (void) setVolume:(float) value 310{ 311 self.gain = value; 312} 313 314- (float) gain 315{ 316 OPTIONALLY_SYNCHRONIZED(self) 317 { 318 return gain; 319 } 320} 321 322- (void) setGain:(float) value 323{ 324 OPTIONALLY_SYNCHRONIZED(self) 325 { 326 if(self.suspended) 327 { 328 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 329 return; 330 } 331 332 gain = value; 333 if(muted) 334 { 335 value = 0; 336 } 337 [ALWrapper sourcef:sourceId parameter:AL_GAIN value:value]; 338 } 339} 340 341@synthesize interruptible; 342 343- (bool) looping 344{ 345 OPTIONALLY_SYNCHRONIZED(self) 346 { 347 return [ALWrapper getSourcei:sourceId parameter:AL_LOOPING]; 348 } 349} 350 351- (void) setLooping:(bool) value 352{ 353 OPTIONALLY_SYNCHRONIZED(self) 354 { 355 if(self.suspended) 356 { 357 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 358 return; 359 } 360 361 [ALWrapper sourcei:sourceId parameter:AL_LOOPING value:value]; 362 } 363} 364 365- (float) maxDistance 366{ 367 OPTIONALLY_SYNCHRONIZED(self) 368 { 369 return [ALWrapper getSourcef:sourceId parameter:AL_MAX_DISTANCE]; 370 } 371} 372 373- (void) setMaxDistance:(float) value 374{ 375 OPTIONALLY_SYNCHRONIZED(self) 376 { 377 if(self.suspended) 378 { 379 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 380 return; 381 } 382 383 [ALWrapper sourcef:sourceId parameter:AL_MAX_DISTANCE value:value]; 384 } 385} 386 387- (float) maxGain 388{ 389 OPTIONALLY_SYNCHRONIZED(self) 390 { 391 return [ALWrapper getSourcef:sourceId parameter:AL_MAX_GAIN]; 392 } 393} 394 395- (void) setMaxGain:(float) value 396{ 397 OPTIONALLY_SYNCHRONIZED(self) 398 { 399 if(self.suspended) 400 { 401 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 402 return; 403 } 404 405 [ALWrapper sourcef:sourceId parameter:AL_MAX_GAIN value:value]; 406 } 407} 408 409- (float) minGain 410{ 411 OPTIONALLY_SYNCHRONIZED(self) 412 { 413 return [ALWrapper getSourcef:sourceId parameter:AL_MIN_GAIN]; 414 } 415} 416 417- (void) setMinGain:(float) value 418{ 419 OPTIONALLY_SYNCHRONIZED(self) 420 { 421 if(self.suspended) 422 { 423 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 424 return; 425 } 426 427 [ALWrapper sourcef:sourceId parameter:AL_MIN_GAIN value:value]; 428 } 429} 430 431- (bool) muted 432{ 433 OPTIONALLY_SYNCHRONIZED(self) 434 { 435 return muted; 436 } 437} 438 439- (void) setMuted:(bool) value 440{ 441 OPTIONALLY_SYNCHRONIZED(self) 442 { 443 if(self.suspended) 444 { 445 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 446 return; 447 } 448 449 muted = value; 450 if(muted) 451 { 452 [self stopActions]; 453 } 454 // Force a re-evaluation of gain. 455 [self setGain:gain]; 456 } 457} 458 459- (float) offsetInBytes 460{ 461 OPTIONALLY_SYNCHRONIZED(self) 462 { 463 return [ALWrapper getSourcef:sourceId parameter:AL_BYTE_OFFSET]; 464 } 465} 466 467- (void) setOffsetInBytes:(float) value 468{ 469 OPTIONALLY_SYNCHRONIZED(self) 470 { 471 if(self.suspended) 472 { 473 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 474 return; 475 } 476 477 [ALWrapper sourcef:sourceId parameter:AL_BYTE_OFFSET value:value]; 478 } 479} 480 481- (float) offsetInSamples 482{ 483 OPTIONALLY_SYNCHRONIZED(self) 484 { 485 return [ALWrapper getSourcef:sourceId parameter:AL_SAMPLE_OFFSET]; 486 } 487} 488 489- (void) setOffsetInSamples:(float) value 490{ 491 OPTIONALLY_SYNCHRONIZED(self) 492 { 493 if(self.suspended) 494 { 495 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 496 return; 497 } 498 499 [ALWrapper sourcef:sourceId parameter:AL_SAMPLE_OFFSET value:value]; 500 } 501} 502 503- (float) offsetInSeconds 504{ 505 OPTIONALLY_SYNCHRONIZED(self) 506 { 507 return [ALWrapper getSourcef:sourceId parameter:AL_SEC_OFFSET]; 508 } 509} 510 511- (void) setOffsetInSeconds:(float) value 512{ 513 OPTIONALLY_SYNCHRONIZED(self) 514 { 515 if(self.suspended) 516 { 517 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 518 return; 519 } 520 521 [ALWrapper sourcef:sourceId parameter:AL_SEC_OFFSET value:value]; 522 } 523} 524 525- (bool) paused 526{ 527 if(self.suspended) 528 { 529 return AL_PAUSED == shadowState; 530 } 531 532 return AL_PAUSED == self.state; 533} 534 535- (void) setPaused:(bool) shouldPause 536{ 537 OPTIONALLY_SYNCHRONIZED(self) 538 { 539 if(self.suspended) 540 { 541 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 542 return; 543 } 544 545 if(shouldPause) 546 { 547 if(AL_PLAYING == self.state) 548 { 549 abortPlaybackResume = YES; 550 if([ALWrapper sourcePause:sourceId]) 551 { 552 shadowState = AL_PAUSED; 553 } 554 } 555 } 556 else 557 { 558 if(AL_PAUSED == self.state) 559 { 560 if([ALWrapper sourcePlay:sourceId]) 561 { 562 shadowState = AL_PLAYING; 563 } 564 else 565 { 566 shadowState = AL_STOPPED; 567 } 568 } 569 } 570 } 571} 572 573- (float) pitch 574{ 575 OPTIONALLY_SYNCHRONIZED(self) 576 { 577 return [ALWrapper getSourcef:sourceId parameter:AL_PITCH]; 578 } 579} 580 581- (void) setPitch:(float) value 582{ 583 OPTIONALLY_SYNCHRONIZED(self) 584 { 585 if(self.suspended) 586 { 587 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 588 return; 589 } 590 591 [ALWrapper sourcef:sourceId parameter:AL_PITCH value:value]; 592 } 593} 594 595- (bool) playing 596{ 597 if(self.suspended) 598 { 599 return AL_PLAYING == shadowState || AL_PAUSED == shadowState; 600 } 601 return AL_PLAYING == self.state || AL_PAUSED == self.state; 602} 603 604- (ALPoint) position 605{ 606 ALPoint result; 607 OPTIONALLY_SYNCHRONIZED(self) 608 { 609 [ALWrapper getSource3f:sourceId parameter:AL_POSITION v1:&result.x v2:&result.y v3:&result.z]; 610 } 611 return result; 612} 613 614- (void) setPosition:(ALPoint) value 615{ 616 OPTIONALLY_SYNCHRONIZED(self) 617 { 618 if(self.suspended) 619 { 620 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 621 return; 622 } 623 624 [ALWrapper source3f:sourceId parameter:AL_POSITION v1:value.x v2:value.y v3:value.z]; 625 } 626} 627 628- (float) pan 629{ 630 return self.position.x; 631} 632 633- (void) setPan:(float) value 634{ 635 if(self.suspended) 636 { 637 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 638 return; 639 } 640 641 self.position = alpoint(value, 0, 0); 642} 643 644- (float) referenceDistance 645{ 646 OPTIONALLY_SYNCHRONIZED(self) 647 { 648 return [ALWrapper getSourcef:sourceId parameter:AL_REFERENCE_DISTANCE]; 649 } 650} 651 652- (void) setReferenceDistance:(float) value 653{ 654 OPTIONALLY_SYNCHRONIZED(self) 655 { 656 if(self.suspended) 657 { 658 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 659 return; 660 } 661 662 [ALWrapper sourcef:sourceId parameter:AL_REFERENCE_DISTANCE value:value]; 663 } 664} 665 666- (float) rolloffFactor 667{ 668 OPTIONALLY_SYNCHRONIZED(self) 669 { 670 return [ALWrapper getSourcef:sourceId parameter:AL_ROLLOFF_FACTOR]; 671 } 672} 673 674- (void) setRolloffFactor:(float) value 675{ 676 OPTIONALLY_SYNCHRONIZED(self) 677 { 678 if(self.suspended) 679 { 680 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 681 return; 682 } 683 684 [ALWrapper sourcef:sourceId parameter:AL_ROLLOFF_FACTOR value:value]; 685 } 686} 687 688@synthesize sourceId; 689 690- (int) sourceRelative 691{ 692 OPTIONALLY_SYNCHRONIZED(self) 693 { 694 return [ALWrapper getSourcei:sourceId parameter:AL_SOURCE_RELATIVE]; 695 } 696} 697 698- (void) setSourceRelative:(int) value 699{ 700 OPTIONALLY_SYNCHRONIZED(self) 701 { 702 if(self.suspended) 703 { 704 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 705 return; 706 } 707 708 [ALWrapper sourcei:sourceId parameter:AL_SOURCE_RELATIVE value:value]; 709 } 710} 711 712- (int) sourceType 713{ 714 OPTIONALLY_SYNCHRONIZED(self) 715 { 716 return [ALWrapper getSourcei:sourceId parameter:AL_SOURCE_TYPE]; 717 } 718} 719 720- (void) setSourceType:(int) value 721{ 722 OPTIONALLY_SYNCHRONIZED(self) 723 { 724 if(self.suspended) 725 { 726 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 727 return; 728 } 729 730 [ALWrapper sourcei:sourceId parameter:AL_SOURCE_TYPE value:value]; 731 } 732} 733 734- (int) state 735{ 736 OPTIONALLY_SYNCHRONIZED(self) 737 { 738 // Apple's OpenAL implementation is broken. 739 //return [ALWrapper getSourcei:sourceId parameter:AL_SOURCE_STATE]; 740 741 if(AL_INITIAL == shadowState || AL_STOPPED == shadowState) 742 { 743 return shadowState; 744 } 745 if(AL_STOPPED == [ALWrapper getSourcei:sourceId parameter:AL_SOURCE_STATE]) 746 { 747 return AL_STOPPED; 748 } 749 return shadowState; 750 } 751} 752 753- (void) setState:(int) value 754{ 755 OPTIONALLY_SYNCHRONIZED(self) 756 { 757 if(self.suspended) 758 { 759 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 760 return; 761 } 762 763 [ALWrapper sourcei:sourceId parameter:AL_SOURCE_STATE value:value]; 764 shadowState = value; 765 } 766} 767 768- (ALVector) velocity 769{ 770 ALVector result; 771 OPTIONALLY_SYNCHRONIZED(self) 772 { 773 [ALWrapper getSource3f:sourceId parameter:AL_VELOCITY v1:&result.x v2:&result.y v3:&result.z]; 774 } 775 return result; 776} 777 778- (void) setVelocity:(ALVector) value 779{ 780 OPTIONALLY_SYNCHRONIZED(self) 781 { 782 if(self.suspended) 783 { 784 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 785 return; 786 } 787 788 [ALWrapper source3f:sourceId parameter:AL_VELOCITY v1:value.x v2:value.y v3:value.z]; 789 } 790} 791 792 793 794#pragma mark Suspend Handler 795 796- (void) addSuspendListener:(id<OALSuspendListener>) listenerIn 797{ 798 [suspendHandler addSuspendListener:listenerIn]; 799} 800 801- (void) removeSuspendListener:(id<OALSuspendListener>) listenerIn 802{ 803 [suspendHandler removeSuspendListener:listenerIn]; 804} 805 806- (bool) manuallySuspended 807{ 808 return suspendHandler.manuallySuspended; 809} 810 811- (void) setManuallySuspended:(bool) value 812{ 813 suspendHandler.manuallySuspended = value; 814} 815 816- (bool) interrupted 817{ 818 return suspendHandler.interrupted; 819} 820 821- (void) setInterrupted:(bool) value 822{ 823 suspendHandler.interrupted = value; 824} 825 826- (bool) suspended 827{ 828 return suspendHandler.suspended; 829} 830 831- (void) setSuspended:(bool) value 832{ 833 if(value) 834 { 835 shadowState = self.state; 836 if(AL_PLAYING == shadowState) 837 { 838 [ALWrapper sourcePause:sourceId]; 839 } 840 } 841 else 842 { 843 // The shadow state holds the state we had when suspending. 844 if(AL_PLAYING == shadowState) 845 { 846 // Because Apple's OpenAL implementation can't stack commands (it defers processing 847 // to a later sequence point), we have to delay resuming playback. 848 abortPlaybackResume = NO; 849 [self performSelector:@selector(delayedResumePlayback) withObject:nil afterDelay:0.03]; 850 } 851 } 852} 853 854- (void) delayedResumePlayback 855{ 856 if(!abortPlaybackResume) 857 { 858 [ALWrapper sourcePlay:sourceId]; 859 } 860} 861 862 863#pragma mark Playback 864 865- (void) preload:(ALBuffer*) bufferIn 866{ 867 OPTIONALLY_SYNCHRONIZED(self) 868 { 869 if(self.suspended) 870 { 871 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 872 return; 873 } 874 875 [self stopActions]; 876 877 if(self.playing || self.paused) 878 { 879 [self stop]; 880 } 881 882 self.buffer = bufferIn; 883 } 884} 885 886- (id<ALSoundSource>) play 887{ 888 OPTIONALLY_SYNCHRONIZED(self) 889 { 890 if(self.suspended) 891 { 892 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 893 return nil; 894 } 895 896 [self stopActions]; 897 898 if(self.playing) 899 { 900 if(!interruptible) 901 { 902 return nil; 903 } 904 [self stop]; 905 } 906 907 if(self.paused) 908 { 909 [self stop]; 910 } 911 912 if([ALWrapper sourcePlay:sourceId]) 913 { 914 shadowState = AL_PLAYING; 915 } 916 else 917 { 918 shadowState = AL_STOPPED; 919 } 920 } 921 return self; 922} 923 924- (id<ALSoundSource>) play:(ALBuffer*) bufferIn 925{ 926 return [self play:bufferIn loop:NO]; 927} 928 929- (id<ALSoundSource>) play:(ALBuffer*) bufferIn loop:(bool) loop 930{ 931 OPTIONALLY_SYNCHRONIZED(self) 932 { 933 if(self.suspended) 934 { 935 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 936 return nil; 937 } 938 939 [self stopActions]; 940 941 if(self.playing) 942 { 943 if(!interruptible) 944 { 945 return nil; 946 } 947 [self stop]; 948 } 949 950 self.buffer = bufferIn; 951 self.looping = loop; 952 953 if([ALWrapper sourcePlay:sourceId]) 954 { 955 shadowState = AL_PLAYING; 956 } 957 else 958 { 959 shadowState = AL_STOPPED; 960 } 961 } 962 return self; 963} 964 965- (id<ALSoundSource>) play:(ALBuffer*) bufferIn gain:(float) gainIn pitch:(float) pitchIn pan:(float) panIn loop:(bool) loopIn 966{ 967 OPTIONALLY_SYNCHRONIZED(self) 968 { 969 if(self.suspended) 970 { 971 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 972 return nil; 973 } 974 975 [self stopActions]; 976 977 if(self.playing) 978 { 979 if(!interruptible) 980 { 981 return nil; 982 } 983 [self stop]; 984 } 985 986 self.buffer = bufferIn; 987 988 // Set gain, pitch, and pan 989 self.gain = gainIn; 990 self.pitch = pitchIn; 991 self.pan = panIn; 992 self.looping = loopIn; 993 994 if([ALWrapper sourcePlay:sourceId]) 995 { 996 shadowState = AL_PLAYING; 997 } 998 else 999 { 1000 shadowState = AL_STOPPED; 1001 } 1002 } 1003 return self; 1004} 1005 1006- (void) stop 1007{ 1008 OPTIONALLY_SYNCHRONIZED(self) 1009 { 1010 if(self.suspended) 1011 { 1012 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1013 return; 1014 } 1015 1016 abortPlaybackResume = YES; 1017 [self stopActions]; 1018 [ALWrapper sourceStop:sourceId]; 1019 shadowState = AL_STOPPED; 1020 } 1021} 1022 1023- (void) rewind 1024{ 1025 OPTIONALLY_SYNCHRONIZED(self) 1026 { 1027 if(self.suspended) 1028 { 1029 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1030 return; 1031 } 1032 1033 abortPlaybackResume = YES; 1034 [self stopActions]; 1035 [ALWrapper sourceRewind:sourceId]; 1036 shadowState = AL_INITIAL; 1037 } 1038} 1039 1040- (void) fadeTo:(float) value 1041 duration:(float) duration 1042 target:(id) target 1043 selector:(SEL) selector 1044{ 1045 // Must always be synchronized 1046 @synchronized(self) 1047 { 1048 if(self.suspended) 1049 { 1050 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1051 return; 1052 } 1053 1054 [self stopFade]; 1055 gainAction = [[OALSequentialActions actions: 1056 [OALGainAction actionWithDuration:duration endValue:value], 1057 [OALCallAction actionWithCallTarget:target selector:selector withObject:self], 1058 nil] retain]; 1059 [gainAction runWithTarget:self]; 1060 } 1061} 1062 1063- (void) stopFade 1064{ 1065 // Must always be synchronized 1066 @synchronized(self) 1067 { 1068 if(self.suspended) 1069 { 1070 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1071 return; 1072 } 1073 1074 [gainAction stopAction]; 1075 [gainAction release]; 1076 gainAction = nil; 1077 } 1078} 1079 1080- (void) panTo:(float) value 1081 duration:(float) duration 1082 target:(id) target 1083 selector:(SEL) selector 1084{ 1085 // Must always be synchronized 1086 @synchronized(self) 1087 { 1088 if(self.suspended) 1089 { 1090 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1091 return; 1092 } 1093 1094 [self stopPan]; 1095 gainAction = [[OALSequentialActions actions: 1096 [OALPanAction actionWithDuration:duration endValue:value], 1097 [OALCallAction actionWithCallTarget:target selector:selector withObject:self], 1098 nil] retain]; 1099 [gainAction runWithTarget:self]; 1100 } 1101} 1102 1103- (void) stopPan 1104{ 1105 // Must always be synchronized 1106 @synchronized(self) 1107 { 1108 if(self.suspended) 1109 { 1110 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1111 return; 1112 } 1113 1114 [gainAction stopAction]; 1115 [gainAction release]; 1116 gainAction = nil; 1117 } 1118} 1119 1120- (void) pitchTo:(float) value 1121 duration:(float) duration 1122 target:(id) target 1123 selector:(SEL) selector 1124{ 1125 // Must always be synchronized 1126 @synchronized(self) 1127 { 1128 if(self.suspended) 1129 { 1130 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1131 return; 1132 } 1133 1134 [self stopPitch]; 1135 gainAction = [[OALSequentialActions actions: 1136 [OALPitchAction actionWithDuration:duration endValue:value], 1137 [OALCallAction actionWithCallTarget:target selector:selector withObject:self], 1138 nil] retain]; 1139 [gainAction runWithTarget:self]; 1140 } 1141} 1142 1143- (void) stopPitch 1144{ 1145 // Must always be synchronized 1146 @synchronized(self) 1147 { 1148 if(self.suspended) 1149 { 1150 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1151 return; 1152 } 1153 1154 [gainAction stopAction]; 1155 [gainAction release]; 1156 gainAction = nil; 1157 } 1158} 1159 1160- (void) stopActions 1161{ 1162 [self stopFade]; 1163 [self stopPan]; 1164 [self stopPitch]; 1165} 1166 1167- (void) clear 1168{ 1169 OPTIONALLY_SYNCHRONIZED(self) 1170 { 1171 self.manuallySuspended = NO; 1172 [self stop]; 1173 self.buffer = nil; 1174 } 1175} 1176 1177 1178#pragma mark Queued Playback 1179 1180- (bool) queueBuffer:(ALBuffer*) bufferIn 1181{ 1182 return [self queueBuffer:bufferIn repeats:0]; 1183} 1184 1185- (bool) queueBuffer:(ALBuffer*) bufferIn repeats:(NSUInteger) repeats 1186{ 1187 OPTIONALLY_SYNCHRONIZED(self) 1188 { 1189 if(self.suspended) 1190 { 1191 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1192 return NO; 1193 } 1194 1195 if(AL_STATIC == self.state) 1196 { 1197 self.buffer = nil; 1198 } 1199 1200 NSUInteger totalTimes = repeats + 1; 1201 ALuint* bufferIds = (ALuint*)malloc(sizeof(ALuint) * totalTimes); 1202 ALuint bufferId = bufferIn.bufferId; 1203 for(NSUInteger i = 0; i < totalTimes; i++) 1204 { 1205 bufferIds[i] = bufferId; 1206 } 1207 bool result = [ALWrapper sourceQueueBuffers:sourceId numBuffers:totalTimes bufferIds:bufferIds]; 1208 free(bufferIds); 1209 return result; 1210 } 1211} 1212 1213- (bool) queueBuffers:(NSArray*) buffers 1214{ 1215 return [self queueBuffers:buffers repeats:0]; 1216} 1217 1218- (bool) queueBuffers:(NSArray*) buffers repeats:(NSUInteger) repeats 1219{ 1220 OPTIONALLY_SYNCHRONIZED(self) 1221 { 1222 if(self.suspended) 1223 { 1224 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1225 return NO; 1226 } 1227 1228 if(AL_STATIC == self.state) 1229 { 1230 self.buffer = nil; 1231 } 1232 1233 NSUInteger numBuffers = [buffers count]; 1234 NSUInteger totalTimes = repeats + 1; 1235 ALuint* bufferIds = (ALuint*)malloc(sizeof(ALuint) * totalTimes * numBuffers); 1236 NSUInteger bufferNum; 1237 1238 for(NSUInteger i = 0; i < totalTimes; i++) 1239 { 1240 bufferNum = 0; 1241 for(ALBuffer* buf in buffers) 1242 { 1243 bufferIds[(i * numBuffers) + bufferNum] = buf.bufferId; 1244 bufferNum++; 1245 } 1246 } 1247 bool result = [ALWrapper sourceQueueBuffers:sourceId numBuffers:totalTimes*numBuffers bufferIds:bufferIds]; 1248 free(bufferIds); 1249 return result; 1250 } 1251} 1252 1253- (bool) unqueueBuffer:(ALBuffer*) bufferIn 1254{ 1255 OPTIONALLY_SYNCHRONIZED(self) 1256 { 1257 if(self.suspended) 1258 { 1259 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1260 return NO; 1261 } 1262 1263 ALuint bufferId = bufferIn.bufferId; 1264 return [ALWrapper sourceUnqueueBuffers:sourceId numBuffers:1 bufferIds:&bufferId]; 1265 } 1266} 1267 1268- (bool) unqueueBuffers:(NSArray*) buffers 1269{ 1270 OPTIONALLY_SYNCHRONIZED(self) 1271 { 1272 if(self.suspended) 1273 { 1274 OAL_LOG_DEBUG(@"%@: Called mutator on suspended object", self); 1275 return NO; 1276 } 1277 1278 if(AL_STATIC == self.state) 1279 { 1280 self.buffer = nil; 1281 } 1282 int numBuffers = [buffers count]; 1283 ALuint* bufferIds = malloc(sizeof(ALuint) * numBuffers); 1284 int i = 0; 1285 for(ALBuffer* buf in buffers) 1286 { 1287 bufferIds[i] = buf.bufferId; 1288 } 1289 bool result = [ALWrapper sourceUnqueueBuffers:sourceId numBuffers:numBuffers bufferIds:bufferIds]; 1290 free(bufferIds); 1291 return result; 1292 } 1293} 1294 1295#pragma mark Internal Use 1296 1297- (bool) requestUnreserve:(bool) interrupt 1298{ 1299 OPTIONALLY_SYNCHRONIZED(self) 1300 { 1301 if(self.playing) 1302 { 1303 if(!self.interruptible || !interrupt) 1304 { 1305 return NO; 1306 } 1307 [self stop]; 1308 } 1309 self.buffer = nil; 1310 } 1311 return YES; 1312} 1313 1314 1315@end