⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 soundengine.cpp

📁 在iPhone OS上使用用Objective-C语言开发的Demo程序
💻 CPP
📖 第 1 页 / 共 4 页
字号:
		if (xFlags & yFlags & kAudioFormatFlagIsFloat) {			xFlags = xFlags & ~kAudioFormatFlagIsSignedInteger;			yFlags = yFlags & ~kAudioFormatFlagIsSignedInteger;		}				//	if the bit depth is 8 bits or less and the format is packed, we don't care about endianness		if((x.mBitsPerChannel <= 8) && ((xFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))		{			xFlags = xFlags & ~kAudioFormatFlagIsBigEndian;		}		if((y.mBitsPerChannel <= 8) && ((yFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))		{			yFlags = yFlags & ~kAudioFormatFlagIsBigEndian;		}				//	if the number of channels is 0 or 1, we don't care about non-interleavedness		if (x.mChannelsPerFrame <= 1 && y.mChannelsPerFrame <= 1) {			xFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;			yFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;		}	}	return xFlags == yFlags;}Boolean FormatIsEqual(AudioStreamBasicDescription x, AudioStreamBasicDescription y){	//	the semantics for equality are:	//		1) Values must match exactly	//		2) wildcard's are ignored in the comparison	#define MATCH(name) ((x.name) == 0 || (y.name) == 0 || (x.name) == (y.name))		return 		((x.mSampleRate==0.) || (y.mSampleRate==0.) || (x.mSampleRate==y.mSampleRate)) 		&& MATCH(mFormatID)		&& MatchFormatFlags(x, y)  		&& MATCH(mBytesPerPacket) 		&& MATCH(mFramesPerPacket) 		&& MATCH(mBytesPerFrame) 		&& MATCH(mChannelsPerFrame) 				&& MATCH(mBitsPerChannel) ;}#pragma mark ***** BackgroundTrackMgr *****//==================================================================================================//	BackgroundTrackMgr class//==================================================================================================class BackgroundTrackMgr{		#define CurFileInfo THIS->mBGFileInfo[THIS->mCurrentFileIndex]	public:		typedef struct BG_FileInfo {			const char*						mFilePath;			AudioFileID						mAFID;			AudioStreamBasicDescription		mFileFormat;			UInt64							mFileDataSize;			//UInt64							mFileNumPackets; // this is only used if loading file to memory			Boolean							mLoadAtOnce;			Boolean							mFileDataInQueue;		} BackgroundMusicFileInfo;				BackgroundTrackMgr() 			:	mQueue(0),				mBufferByteSize(0),				mCurrentPacket(0),				mNumPacketsToRead(0),				mVolume(1.0),				mPacketDescs(NULL),				mCurrentFileIndex(0),				mMakeNewQueueWhenStopped(false),				mStopAtEnd(false) { }				~BackgroundTrackMgr() { Teardown(); }		void Teardown()		{			if (mQueue)				AudioQueueDispose(mQueue, true);			for (UInt32 i=0; i < mBGFileInfo.size(); i++)				if (mBGFileInfo[i]->mAFID)					AudioFileClose(mBGFileInfo[i]->mAFID);			if (mPacketDescs)				delete mPacketDescs;		}				AudioStreamPacketDescription *GetPacketDescsPtr() { return mPacketDescs; }				UInt32 GetNumPacketsToRead(BackgroundTrackMgr::BG_FileInfo *inFileInfo) 		{ 			return mNumPacketsToRead; 		}		static OSStatus AttachNewCookie(AudioQueueRef inQueue, BackgroundTrackMgr::BG_FileInfo *inFileInfo)		{			OSStatus result = noErr;			UInt32 size = sizeof(UInt32);			result = AudioFileGetPropertyInfo (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, NULL);			if (!result && size) 			{				char* cookie = new char [size];						result = AudioFileGetProperty (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, cookie);					AssertNoError("Error getting cookie data", end);				result = AudioQueueSetProperty(inQueue, kAudioQueueProperty_MagicCookie, cookie, size);				delete [] cookie;					AssertNoError("Error setting cookie data for queue", end);			}			return noErr;				end:			return noErr;		}		static void QueueStoppedProc(	void *                  inUserData,										AudioQueueRef           inAQ,										AudioQueuePropertyID    inID)		{			UInt32 isRunning;			UInt32 propSize = sizeof(isRunning);			BackgroundTrackMgr *THIS = (BackgroundTrackMgr*)inUserData;			OSStatus result = AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &isRunning, &propSize);							if ((!isRunning) && (THIS->mMakeNewQueueWhenStopped))			{				result = AudioQueueDispose(inAQ, true);					AssertNoError("Error disposing queue", end);				result = THIS->SetupQueue(CurFileInfo);					AssertNoError("Error setting up new queue", end);				result = THIS->SetupBuffers(CurFileInfo);					AssertNoError("Error setting up new queue buffers", end);				result = THIS->Start();					AssertNoError("Error starting queue", end);			}		end:			return;		}				static Boolean DisposeBuffer(AudioQueueRef inAQ, std::vector<AudioQueueBufferRef> inDisposeBufferList, AudioQueueBufferRef inBufferToDispose)		{			for (unsigned int i=0; i < inDisposeBufferList.size(); i++)			{				if (inBufferToDispose == inDisposeBufferList[i])				{					OSStatus result = AudioQueueFreeBuffer(inAQ, inBufferToDispose);					if (result == noErr)						inDisposeBufferList.pop_back();					return true;				}			}			return false;		}				enum {			kQueueState_DoNothing		= 0,			kQueueState_ResizeBuffer	= 1,			kQueueState_NeedNewCookie	= 2,			kQueueState_NeedNewBuffers	= 3,			kQueueState_NeedNewQueue	= 4,		};				static SInt8 GetQueueStateForNextBuffer(BackgroundTrackMgr::BG_FileInfo *inFileInfo, BackgroundTrackMgr::BG_FileInfo *inNextFileInfo)		{			inFileInfo->mFileDataInQueue = false;						// unless the data formats are the same, we need a new queue			if (!FormatIsEqual(inFileInfo->mFileFormat, inNextFileInfo->mFileFormat))				return kQueueState_NeedNewQueue;							// if going from a load-at-once file to streaming or vice versa, we need new buffers			if (inFileInfo->mLoadAtOnce != inNextFileInfo->mLoadAtOnce)				return kQueueState_NeedNewBuffers;						// if the next file is smaller than the current, we just need to resize			if (inNextFileInfo->mLoadAtOnce)				return (inFileInfo->mFileDataSize >= inNextFileInfo->mFileDataSize) ? kQueueState_ResizeBuffer : kQueueState_NeedNewBuffers;							return kQueueState_NeedNewCookie;		}				static void QueueCallback(	void *					inUserData,									AudioQueueRef			inAQ,									AudioQueueBufferRef		inCompleteAQBuffer) 		{			// dispose of the buffer if no longer in use			OSStatus result = noErr;			BackgroundTrackMgr *THIS = (BackgroundTrackMgr*)inUserData;			if (DisposeBuffer(inAQ, THIS->mBuffersToDispose, inCompleteAQBuffer))				return;						UInt32 nPackets = 0;			// loop the current buffer if the following:			// 1. file was loaded into the buffer previously			// 2. only one file in the queue			// 3. we have not been told to stop at playlist completion			if ((CurFileInfo->mFileDataInQueue) && (THIS->mBGFileInfo.size() == 1) && (!THIS->mStopAtEnd))				nPackets = THIS->GetNumPacketsToRead(CurFileInfo);			else			{				UInt32 numBytes;				while (nPackets == 0)				{					// if loadAtOnce, get all packets in the file, otherwise ~.5 seconds of data					nPackets = THIS->GetNumPacketsToRead(CurFileInfo);										result = AudioFileReadPackets(CurFileInfo->mAFID, false, &numBytes, THIS->mPacketDescs, THIS->mCurrentPacket, &nPackets, 											inCompleteAQBuffer->mAudioData);						AssertNoError("Error reading file data", end);										inCompleteAQBuffer->mAudioDataByteSize = numBytes;																	if (nPackets == 0) // no packets were read, this file has ended.					{						if (CurFileInfo->mLoadAtOnce)							CurFileInfo->mFileDataInQueue = true;												THIS->mCurrentPacket = 0;						UInt32 theNextFileIndex = (THIS->mCurrentFileIndex < THIS->mBGFileInfo.size()-1) ? THIS->mCurrentFileIndex+1 : 0;												// we have gone through the playlist. if mStopAtEnd, stop the queue here						if (theNextFileIndex == 0 && THIS->mStopAtEnd)						{							result = AudioQueueStop(inAQ, false);								AssertNoError("Error stopping queue", end);							return;						}												SInt8 theQueueState = GetQueueStateForNextBuffer(CurFileInfo, THIS->mBGFileInfo[theNextFileIndex]);						if (theNextFileIndex != THIS->mCurrentFileIndex)						{							// if were are not looping the same file. Close the old one and open the new							result = AudioFileClose(CurFileInfo->mAFID);								AssertNoError("Error closing file", end);							THIS->mCurrentFileIndex = theNextFileIndex;							result = LoadFileDataInfo(CurFileInfo->mFilePath, CurFileInfo->mAFID, CurFileInfo->mFileFormat, CurFileInfo->mFileDataSize);								AssertNoError("Error opening file", end);						}												switch (theQueueState) 						{														// if we need to resize the buffer, set the buffer's audio data size to the new file's size							// we will also need to get the new file cookie							case kQueueState_ResizeBuffer:								inCompleteAQBuffer->mAudioDataByteSize = CurFileInfo->mFileDataSize;														// if the data format is the same but we just need a new cookie, attach a new cookie							case kQueueState_NeedNewCookie:								result = AttachNewCookie(inAQ, CurFileInfo);									AssertNoError("Error attaching new file cookie data to queue", end);								break;														// we can keep the same queue, but not the same buffer(s)							case kQueueState_NeedNewBuffers:								THIS->mBuffersToDispose.push_back(inCompleteAQBuffer);								THIS->SetupBuffers(CurFileInfo);								break;														// if the data formats are not the same, we need to dispose the current queue and create a new one							case kQueueState_NeedNewQueue:								THIS->mMakeNewQueueWhenStopped = true;								result = AudioQueueStop(inAQ, false);									AssertNoError("Error stopping queue", end);								return;															default:								break;						}					}				}			}						result = AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, (THIS->mPacketDescs ? nPackets : 0), THIS->mPacketDescs);				AssertNoError("Error enqueuing new buffer", end);			if (CurFileInfo->mLoadAtOnce)				CurFileInfo->mFileDataInQueue = true;							THIS->mCurrentPacket += nPackets;				end:			return;		}				OSStatus SetupQueue(BG_FileInfo *inFileInfo)		{			UInt32 size = 0;			OSStatus result = AudioQueueNewOutput(&inFileInfo->mFileFormat, QueueCallback, this, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &mQueue);					AssertNoError("Error creating queue", end);			// (2) If the file has a cookie, we should get it and set it on the AQ			size = sizeof(UInt32);			result = AudioFileGetPropertyInfo (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, NULL);			if (!result && size) {				char* cookie = new char [size];						result = AudioFileGetProperty (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, cookie);					AssertNoError("Error getting magic cookie", end);				result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_MagicCookie, cookie, size);				delete [] cookie;					AssertNoError("Error setting magic cookie", end);			}			// channel layout			OSStatus err = AudioFileGetPropertyInfo(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, NULL);			if (err == noErr && size > 0) {				AudioChannelLayout *acl = (AudioChannelLayout *)malloc(size);				result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, acl);					AssertNoError("Error getting channel layout from file", end);				result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_ChannelLayout, acl, size);				free(acl);					AssertNoError("Error setting channel layout on queue", end);			}						// add a notification proc for when the queue stops			result = AudioQueueAddPropertyListener(mQueue, kAudioQueueProperty_IsRunning, QueueStoppedProc, this);				AssertNoError("Error adding isRunning property listener to queue", end);							// we need to reset this variable so that if the queue is stopped mid buffer we don't dispose it 			mMakeNewQueueWhenStopped = false;						// volume			result = SetVolume(mVolume);					end:			return result;		}		OSStatus SetupBuffers(BG_FileInfo *inFileInfo)		{			int numBuffersToQueue = kNumberBuffers;			UInt32 maxPacketSize;			UInt32 size = sizeof(maxPacketSize);			// we need to calculate how many packets we read at a time, and how big a buffer we need			// we base this on the size of the packets in the file and an approximate duration for each buffer							// first check to see what the max size of a packet is - if it is bigger			// than our allocation default size, that needs to become larger			OSStatus result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);				AssertNoError("Error getting packet upper bound size", end);			bool isFormatVBR = (inFileInfo->mFileFormat.mBytesPerPacket == 0 || inFileInfo->mFileFormat.mFramesPerPacket == 0);			CalculateBytesForTime(inFileInfo->mFileFormat, maxPacketSize, 0.5/*seconds*/, &mBufferByteSize, &mNumPacketsToRead);						// if the file is smaller than the capacity of all the buffer queues, always load it at once			if ((mBufferByteSize * numBuffersToQueue) > inFileInfo->mFileDataSize)				inFileInfo->mLoadAtOnce = true;							if (inFileInfo->mLoadAtOnce)			{				UInt64 theFileNumPackets;				size = sizeof(UInt64);				result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyAudioDataPacketCount, &size, &theFileNumPackets);					AssertNoError("Error getting packet count for file", end);								mNumPacketsToRead = (UInt32)theFileNumPackets;				mBufferByteSize = inFileInfo->mFileDataSize;				numBuffersToQueue = 1;			}				else			{				mNumPacketsToRead = mBufferByteSize / maxPacketSize;			}						if (isFormatVBR)				mPacketDescs = new AudioStreamPacketDescription [mNumPacketsToRead];			else				mPacketDescs = NULL; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)								// allocate the queue's buffers			for (int i = 0; i < numBuffersToQueue; ++i) 			{				result = AudioQueueAllocateBuffer(mQueue, mBufferByteSize, &mBuffers[i]);					AssertNoError("Error allocating buffer for queue", end);				QueueCallback (this, mQueue, mBuffers[i]);				if (inFileInfo->mLoadAtOnce)					inFileInfo->mFileDataInQueue = true;			}				end:			return result;		}				OSStatus LoadTrack(const char* inFilePath, Boolean inAddToQueue, Boolean inLoadAtOnce)		{			BG_FileInfo *fileInfo = new BG_FileInfo;			fileInfo->mFilePath = inFilePath;			OSStatus result = LoadFileDataInfo(fileInfo->mFilePath, fileInfo->mAFID, fileInfo->mFileFormat, fileInfo->mFileDataSize);				AssertNoError("Error getting file data info", fail);			fileInfo->mLoadAtOnce = inLoadAtOnce;			fileInfo->mFileDataInQueue = false;			// if not adding to the queue, clear the current file vector			if (!inAddToQueue)				mBGFileInfo.clear();							mBGFileInfo.push_back(fileInfo);						// setup the queue if this is the first (or only) file			if (mBGFileInfo.size() == 1)			{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -