ios CCAudio.mm

//
//  CCAudio.h
//  CCFC
//
//  Created by xichen on 11-12-18.
//  Copyright 2011 ccteam. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>

@interface CCAudio : NSObject 
{
 
}

+ (int)playSound:(NSString *)soundFullPath;
+ (void)playSystemSound:(uint)sysSoundId;
+ (void)playSystemKeyboardClick;

@end


// 歌曲的播放状态
typedef enum _CCAudioPlayerPlayingStatus
{
	CC_AP_PLAYING_STATUS_INITING,
	CC_AP_PLAYING_STATUS_INIT_OK,
	CC_AP_PLAYING_STATUS_PLAYING,
	CC_AP_PLAYING_STATUS_PAUSED,
	CC_AP_PLAYING_STATUS_STOPPED,					
	CC_AP_PLAYING_STATUS_UNKOWN,
	
	CC_AP_SONG_STATUS_MAX
}CCAudioPlayerPlayingStatus;


@class CCLocalAudioPlayer;


#define	kNumberBuffers		3

// a player for playing local file
@interface CCLocalAudioPlayer : NSObject 
{	
	NSString							*_path;
	
@private
	AudioQueueRef						_queue;
	AudioFileID							_audioFile;
	AudioStreamBasicDescription			_dataFormat;
	UInt32								_numPacketsToRead;
	AudioQueueBufferRef					_buffers[kNumberBuffers];
	SInt64								_currentPacket;
	float								_volume;
	CCAudioPlayerPlayingStatus			_playStatus;
	
@public
	UInt32								_isRunning;
}

@property (nonatomic, copy)		NSString							*path;
@property (nonatomic, assign)	UInt32								numPacketsToRead;
@property (nonatomic, assign)	AudioFileID							audioFile;
@property (nonatomic, assign)	SInt64								currentPacket;
@property (nonatomic, assign)	UInt32								isRunning;

- (id)initWithPath:(NSString *)path;
- (void)dealloc;

//播放控制
- (OSStatus)play;
- (OSStatus)pause;
- (OSStatus)resume;
- (OSStatus)stop;

//音量控制
- (float)getVolume;
- (OSStatus)setVolume:(float)newVolume;

- (int)getDuration;					// 歌曲总时长,以秒为单位
- (int)getCurrentPlayTime;			// 歌曲当前播放的时间

- (BOOL)isPlaying;								// 返回是否处于播放状态
- (CCAudioPlayerPlayingStatus)getPlayStatus;	// 返回播放状态


@end


// it doesn't support for amr recording
@interface CCRecorder : NSObject 
{	
	NSString							*_path;
	
@private
	AudioQueueRef						_queue;
	AudioFileID							_recordFile;
	AudioStreamBasicDescription			_mRecordFormat;
	AudioQueueBufferRef					_buffers[kNumberBuffers];
	SInt64								_mRecordPacket;
	
@public
	BOOL								_isRunning;
}

@property (nonatomic, copy)		NSString							*path;

- (id)initWithPath:(NSString *)path;
- (void)dealloc;

- (void)setupCommonAudioFormat:(UInt32)formatID;
- (void)setRecordToPCM;

- (OSStatus)start;
- (void)stop;

- (AudioFileID)getRecordFile;
- (SInt64)getRecordPacket;

@end



 

//
//  CCAudio.mm
//  CCFC
//
//  Created by xichen on 11-12-18.
//  Copyright 2011 ccteam. All rights reserved.
//

#import "CCAudio.h"
#import <AudioToolbox/AudioToolbox.h>

@implementation CCAudio

+ (int)playSound:(NSString *)soundFullPath
{
	SystemSoundID soundId; 
	NSURL *filePath = [NSURL fileURLWithPath:soundFullPath isDirectory:NO];
	
	OSStatus status = AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundId);
	if(status != 0)
		return status;
	
	AudioServicesPlaySystemSound(soundId);
	return status;
}

+ (void)playSystemSound:(uint)sysSoundId
{
	AudioServicesPlaySystemSound(sysSoundId);
}

+ (void)playSystemKeyboardClick
{
	AudioServicesPlaySystemSound(0x450);
}


@end


void onBufferCallback(void					*inUserData,
					  AudioQueueRef			inAQ,
					  AudioQueueBufferRef	inCompleteAQBuffer)
{
	CCLocalAudioPlayer *player = (CCLocalAudioPlayer *)inUserData;
	
	UInt32 numBytes;
	UInt32 nPackets = player.numPacketsToRead;
	OSStatus status = AudioFileReadPackets(player.audioFile, false, 
										   &numBytes, 
										   inCompleteAQBuffer->mPacketDescriptions, 
										   player.currentPacket, 
										   &nPackets, 
										   inCompleteAQBuffer->mAudioData);
	if (status)
		printf("AudioFileReadPackets failed: %d", (int)status);
	if (nPackets > 0) 
	{
		inCompleteAQBuffer->mAudioDataByteSize = numBytes;		
		inCompleteAQBuffer->mPacketDescriptionCount = nPackets;		
		status = AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, 0, NULL);
		if(status != 0)
		{
			printf("AudioQueueEnqueueBuffer failed: %d", (int)status);
			return;
		}
		player.currentPacket += nPackets;
	} 	
	else 
	{
		status = AudioQueueStop(inAQ, false);
		if(status != 0)
		{
			printf("AudioQueueStop failed: %d", (int)status);
			return;
		}
	}
}

void isRunningProc(void						*inUserData,
				   AudioQueueRef			inAQ,
				   AudioQueuePropertyID		inID)
{
	CCLocalAudioPlayer *player = (CCLocalAudioPlayer *)inUserData;
	UInt32 size = 4;
	OSStatus status = AudioQueueGetProperty(inAQ, 
											kAudioQueueProperty_IsRunning, 
											&player->_isRunning, 
											&size);
	if(status != 0)
	{
		printf("AudioQueueGetProperty failed: %d", (int)status);
		return;
	}
	if (!player.isRunning)
		[[NSNotificationCenter defaultCenter] postNotificationName:@"playbackQueueStopped" object:nil];
}


@implementation CCLocalAudioPlayer

@synthesize path = _path;
@synthesize numPacketsToRead = _numPacketsToRead;
@synthesize audioFile = _audioFile;
@synthesize currentPacket = _currentPacket;
@synthesize isRunning = _isRunning;

- (void)calculateBytesForTime:(AudioStreamBasicDescription *)inDesc 
				maxPacketSize:(UInt32)inMaxPacketSize 
					  seconds:(Float64)inSeconds 
				outBufferSize:(UInt32 *)outBufferSize 
				outNumPackets:(UInt32 *)outNumPackets
{
	static const int maxBufferSize = 0x10000;	//	64K
	static const int minBufferSize = 0x4000;	//	16K
	
	if (inDesc->mFramesPerPacket) 
	{
		Float64 numPacketsForTime = inDesc->mSampleRate / inDesc->mFramesPerPacket * inSeconds;
		*outBufferSize = numPacketsForTime * inMaxPacketSize;
	} else 
	{
		*outBufferSize = maxBufferSize > inMaxPacketSize ? maxBufferSize : inMaxPacketSize;
	}
	
	if (*outBufferSize > maxBufferSize && *outBufferSize > inMaxPacketSize)
		*outBufferSize = maxBufferSize;
	else 
	{
		if (*outBufferSize < minBufferSize)
			*outBufferSize = minBufferSize;
	}
	*outNumPackets = *outBufferSize / inMaxPacketSize;
}


- (id)initWithPath:(NSString *)path
{
	self = [super init];
	if(self)
	{
		self.path = path;
		_playStatus = CC_AP_PLAYING_STATUS_UNKOWN;
	}
	return self;
}

- (void)dealloc
{
	[_path release];
	
	if (_queue)
	{
		AudioQueueDispose(_queue, true);
		_queue = NULL;
	}
	if (_audioFile)
	{		
		AudioFileClose(_audioFile);
		_audioFile = 0;
	}
	
	[super dealloc];
}


//播放控制
- (OSStatus)play
{
	char	*cookie = NULL;
	AudioChannelLayout *acl = NULL;
	BOOL isFormatVBR;
	UInt32 size;
	
	_playStatus = CC_AP_PLAYING_STATUS_INITING;
	
	OSStatus status = AudioFileOpenURL((CFURLRef)[NSURL fileURLWithPath:_path]
					 , kAudioFileReadPermission
					 , 0
					 , &_audioFile);
	if(status != 0)
		goto end;
	
	size = sizeof(_dataFormat);
	status = AudioFileGetProperty(_audioFile, 
						 kAudioFilePropertyDataFormat, &size, &_dataFormat);
	if(status != 0)
		goto end;
	
	status = AudioQueueNewOutput(&_dataFormat, onBufferCallback, self, 
						CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &_queue);
	if(status != 0)
		goto end;
	
	UInt32 bufferByteSize;		
	UInt32 maxPacketSize;
	size = sizeof(maxPacketSize);
	status = AudioFileGetProperty(_audioFile, 
				kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
	if(status != 0)
		goto end;
	
	[self calculateBytesForTime:&_dataFormat
				  maxPacketSize:maxPacketSize
						seconds:0.5f
				  outBufferSize:&bufferByteSize
				  outNumPackets:&_numPacketsToRead];

	size = sizeof(UInt32);
	status = AudioFileGetPropertyInfo (_audioFile, kAudioFilePropertyMagicCookieData, &size, NULL);
	
	if (!status && size) 
	{
		cookie = new char [size];
		status = AudioFileGetProperty (_audioFile, kAudioFilePropertyMagicCookieData, &size, cookie);
		if(status != 0)
			goto end;
		status = AudioQueueSetProperty(_queue, kAudioQueueProperty_MagicCookie, cookie, size);
		if(status != 0)
			goto end;
		delete []cookie;
		cookie = NULL;
	}
	
	status = AudioFileGetPropertyInfo(_audioFile, kAudioFilePropertyChannelLayout, &size, NULL);
	if (status != 0 && size > 0) 
	{
		acl = (AudioChannelLayout *)malloc(size);
		status = AudioFileGetProperty(_audioFile, kAudioFilePropertyChannelLayout, &size, acl);
		if(status != 0)
			goto end;
		status = AudioQueueSetProperty(_queue, kAudioQueueProperty_ChannelLayout, acl, size);
		if(status != 0)
			goto end;
		free(acl);
		acl = NULL;
	}
	
	status = AudioQueueAddPropertyListener(_queue, kAudioQueueProperty_IsRunning, 
								  isRunningProc, self);
	if(status != 0)
		goto end;
	
	isFormatVBR = (_dataFormat.mBytesPerPacket == 0 
						|| _dataFormat.mFramesPerPacket == 0);
	for (int i = 0; i < kNumberBuffers; ++i) 
	{
		status = AudioQueueAllocateBufferWithPacketDescriptions(_queue, 
										bufferByteSize, 
										(isFormatVBR ? _numPacketsToRead : 0), 
										&_buffers[i]);
		if(status != 0)
			goto end;
	}	
	
	status = AudioQueueSetParameter(_queue, kAudioQueueParam_Volume, 1.0);
	if(status != 0)
		goto end;
	
	for (int i = 0; i < kNumberBuffers; ++i) 
	{
		onBufferCallback(self, _queue, _buffers[i]);			
	}
	_playStatus = CC_AP_PLAYING_STATUS_INIT_OK;
	
	
	status = AudioQueueStart(_queue, NULL);
	if(status == 0)
		_playStatus = CC_AP_PLAYING_STATUS_PLAYING;
	else
	{
		printf("CCAudio play failed\n");
	}
		
	return status;
	
end:
	printf("CCAudio play failed\n");
	delete []cookie;
	free(acl);
	return status;
}

- (OSStatus)pause
{
	OSStatus status = AudioQueuePause(_queue);
	if (status != 0)
	{
		printf("CCAudio pause failed\n");
	}
	else
	{
		_playStatus = CC_AP_PLAYING_STATUS_PAUSED;
	}
	return status;
}

- (OSStatus)resume
{
	OSStatus status = AudioQueueStart(_queue, NULL);
	if (status != 0)
	{
		printf("CCAudio resume failed\n");
	}
	else
	{
		_playStatus = CC_AP_PLAYING_STATUS_PLAYING;
	}
	return status;
}

- (OSStatus)stop
{
	OSStatus status = AudioQueueStop(_queue, true);
	if (status != 0)
	{
		printf("CCAudio stop failed\n");
	}
	else
	{
		_playStatus = CC_AP_PLAYING_STATUS_STOPPED;
	}
	
	if (_queue)
	{
		AudioQueueDispose(_queue, true);
		_queue = NULL;
	}
	if (_audioFile)
	{		
		AudioFileClose(_audioFile);
		_audioFile = 0;
	}
	
	memset(_buffers, 0, kNumberBuffers * sizeof(AudioQueueBufferRef));
	_numPacketsToRead = 0;
	_currentPacket = 0;
	
	return status;
}

//音量控制
- (float)getVolume
{
	return _volume;
}

- (OSStatus)setVolume:(float)newVolume
{
	OSStatus status = AudioQueueSetParameter(_queue, kAudioQueueParam_Volume, newVolume);	
	if(status != noErr)
	{
		return status;
	}
	_volume = newVolume;
	return noErr;
}

- (int)getDuration		// 歌曲总时长,以秒为单位
{
	NSTimeInterval duration = 0;
	UInt32 size = sizeof(duration);
	
	OSStatus status;
	status = AudioFileGetProperty(_audioFile, 
								  kAudioFilePropertyEstimatedDuration, 
								  &size, 
								  &duration);
	if(status != noErr)
	{
		NSLog(@"****CCAudio getDuration error!");
		return -1;
	}
	return (int)duration;
}

- (int)getCurrentPlayTime			// 歌曲当前播放的时间
{
	int currentTime = -1;
			
	AudioTimeStamp queueTime;
	Boolean discontinuity;
	
	OSStatus status;
	status = AudioQueueGetCurrentTime(_queue, NULL, &queueTime, &discontinuity);
	if (status == noErr)
	{
		double temp = queueTime.mSampleTime / _dataFormat.mSampleRate;
		if (temp < 0.0)
		{
			temp = 0.0;
		}
		
		currentTime = (int)temp;
	}
	return currentTime;
}

- (BOOL)isPlaying								// 返回是否处于播放状态
{
	return _playStatus == CC_AP_PLAYING_STATUS_PLAYING;
}

- (CCAudioPlayerPlayingStatus)getPlayStatus	// 返回播放状态
{
	return _playStatus;
}


@end


@interface CCRecorder(privateApi)

- (void)setRecordPacket:(SInt64)newRecordPacket;
- (BOOL)isRunning;

@end

// CCRecorder

void inputBufferHandler(void *								inUserData,
						AudioQueueRef						inAQ,
						AudioQueueBufferRef					inBuffer,
						const AudioTimeStamp 				*inStartTime,
						UInt32								inNumPackets,
						const AudioStreamPacketDescription	*inPacketDesc)
{
	CCRecorder *recorder = (CCRecorder *)inUserData;
	if (inNumPackets > 0) 
	{
		NSLog(@"CCRecorder inputBufferHandler: inNumPackets:%d", inNumPackets);
		AudioFileWritePackets([recorder getRecordFile], 
							  FALSE, 
							  inBuffer->mAudioDataByteSize,
							  inPacketDesc, 
							  [recorder getRecordPacket], 
							  &inNumPackets, 
							  inBuffer->mAudioData);
		[recorder setRecordPacket:[recorder getRecordPacket] + inNumPackets];
	}
	
	if ([recorder isRunning])
		AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
}

@implementation CCRecorder

@synthesize path = _path;

- (id)initWithPath:(NSString *)path
{
	self = [super init];
	if(self)
	{
		self.path = path;
	}
	return self;
}

- (void)dealloc
{
	[_path release];
	
	AudioQueueDispose(_queue, true);
	AudioFileClose(_recordFile);
	
	[super dealloc];
}

- (void)setupCommonAudioFormat:(UInt32)formatID
{
	memset(&_mRecordFormat, 0, sizeof(_mRecordFormat));
	
	UInt32 size = sizeof(_mRecordFormat.mSampleRate);
	AudioSessionGetProperty(	
							kAudioSessionProperty_CurrentHardwareSampleRate,
							&size, 
							&_mRecordFormat.mSampleRate);
		
	size = sizeof(_mRecordFormat.mChannelsPerFrame);
	AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, 
							&size, 
							&_mRecordFormat.mChannelsPerFrame);
	
	_mRecordFormat.mFormatID = formatID;
	if (formatID == kAudioFormatLinearPCM)
	{
		_mRecordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
		_mRecordFormat.mBitsPerChannel = 16;
		_mRecordFormat.mBytesPerPacket = 2;
		_mRecordFormat.mChannelsPerFrame = 1;
		_mRecordFormat.mBytesPerFrame = (_mRecordFormat.mBitsPerChannel / 8) * _mRecordFormat.mChannelsPerFrame;
		_mRecordFormat.mFramesPerPacket = 1;
		_mRecordFormat.mSampleRate = 16000;
		
	}
}

- (void)setRecordToPCM
{
	[self setupCommonAudioFormat:kAudioFormatLinearPCM];
}


- (void)copyEncoderCookieToFile
{
	UInt32 propertySize;	
	OSStatus status = AudioQueueGetPropertySize(_queue, kAudioQueueProperty_MagicCookie, &propertySize);
	
	if (status == noErr && propertySize > 0) 
	{
		Byte *magicCookie = new Byte[propertySize];
		UInt32 magicCookieSize;
		AudioQueueGetProperty(_queue, kAudioQueueProperty_MagicCookie, magicCookie, &propertySize);
		magicCookieSize = propertySize;	
		
		UInt32 willEatTheCookie = false;
		status = AudioFileGetPropertyInfo(_recordFile, kAudioFilePropertyMagicCookieData, NULL, &willEatTheCookie);
		if (status == noErr && willEatTheCookie) 
		{
			status = AudioFileSetProperty(_recordFile, kAudioFilePropertyMagicCookieData, magicCookieSize, magicCookie);
		}
		delete[] magicCookie;
	}
}

- (int)computeRecordBufferSize:(const AudioStreamBasicDescription *)format
					   seconds:(float)seconds
{
	int packets, frames, bytes = 0;

	frames = (int)ceil(seconds * format->mSampleRate);
	
	if (format->mBytesPerFrame > 0)
		bytes = frames * format->mBytesPerFrame;
	else 
	{
		UInt32 maxPacketSize;
		if (format->mBytesPerPacket > 0)
			maxPacketSize = format->mBytesPerPacket;	
		else {
			UInt32 propertySize = sizeof(maxPacketSize);
			OSStatus status = AudioQueueGetProperty(_queue, kAudioQueueProperty_MaximumOutputPacketSize, &maxPacketSize,
												&propertySize);
			if(status != noErr)
				return 0;
		}
		if (format->mFramesPerPacket > 0)
			packets = frames / format->mFramesPerPacket;
		else
			packets = frames;	
		if (packets == 0)		
			packets = 1;
		bytes = packets * maxPacketSize;
	}	
	return bytes;
}


- (OSStatus)start
{
	OSStatus status;
	UInt32 size;
	
	status = AudioQueueNewInput(
						&_mRecordFormat,
						inputBufferHandler,
						self, 
						NULL, 
						NULL,
						0,
						&_queue);
	
	if(status != noErr)
		return status;
	
	_mRecordPacket = 0;
	
	size = sizeof(_mRecordFormat);
	status = AudioQueueGetProperty(_queue, kAudioQueueProperty_StreamDescription,	
										&_mRecordFormat, &size);
	if(status != noErr)
		return status;
	
	status = AudioFileCreateWithURL((CFURLRef)[NSURL fileURLWithPath:_path], 
						   kAudioFileCAFType, 
						   &_mRecordFormat, 
						   kAudioFileFlags_EraseFile,
						   &_recordFile);
	if(status != noErr)
		return status;
	[self copyEncoderCookieToFile];
	
	int bufferByteSize = [self computeRecordBufferSize:&_mRecordFormat seconds:0.5f];
	
	for (int i = 0; i < kNumberBuffers; ++i) 
	{
		status = AudioQueueAllocateBuffer(_queue, bufferByteSize, &_buffers[i]);
		if(status != noErr)
			return status;
		
		status = AudioQueueEnqueueBuffer(_queue, _buffers[i], 0, NULL);
		if(status != noErr)
			return status;
	}
	
	status = AudioQueueStart(_queue, NULL);
	if(status == noErr)
		_isRunning = TRUE;
	
	return status;
}

- (void)stop
{
	AudioQueueStop(_queue, true);
	
	[self copyEncoderCookieToFile];
	
	AudioQueueDispose(_queue, true);
	AudioFileClose(_recordFile);
	
	_isRunning = FALSE;
}

- (BOOL)isRunning
{
	return _isRunning;
}

- (AudioFileID)getRecordFile
{
	return _recordFile;
}

- (SInt64)getRecordPacket
{
	return _mRecordPacket;
}

- (void)setRecordPacket:(SInt64)newRecordPacket
{
	_mRecordPacket = newRecordPacket;
}

@end



 


微风不燥,阳光正好,你就像风一样经过这里,愿你停留的片刻温暖舒心。

我是程序员小迷(致力于C、C++、Java、Kotlin、Android、Shell、JavaScript、TypeScript、Python等编程技术的技巧经验分享),若作品对您有帮助,请关注、分享、点赞、收藏、在看、喜欢,您的支持是我们为您提供帮助的最大动力。

欢迎关注。助您在编程路上越走越好!

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-12 20:24:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-12 20:24:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-12 20:24:01       82 阅读
  4. Python语言-面向对象

    2024-06-12 20:24:01       91 阅读

热门阅读

  1. Codeforces 1354B

    2024-06-12 20:24:01       31 阅读
  2. html 使用input file上传图片并显示

    2024-06-12 20:24:01       32 阅读
  3. 软件许可证管理?

    2024-06-12 20:24:01       27 阅读
  4. 如何有效限制IP多次重新访问网站

    2024-06-12 20:24:01       32 阅读
  5. MySQL 运算符绕过

    2024-06-12 20:24:01       25 阅读