PageRenderTime 119ms CodeModel.GetById 15ms app.highlight 97ms RepoModel.GetById 1ms app.codeStats 1ms

/libs/ObjectAL/OpenAL/ALSource.m

http://github.com/kstenerud/ObjectAL-for-iPhone
Objective C | 1315 lines | 1072 code | 190 blank | 53 comment | 104 complexity | 117557c0304ae421211838b55dc30765 MD5 | raw file
   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