這個類可以讓你在給定頻率播放提示音,並與給定的幅度。 它使用來自的AudioQueues AudioToolbox.framework。這只是一個草圖,許多事情應該細化,但創建信號的機制起作用。
如果您看到@interface
,用法非常簡單。
#import <AudioToolbox/AudioToolbox.h>
#define TONE_SAMPLERATE 44100.
@interface Tone : NSObject {
AudioQueueRef queue;
AudioQueueBufferRef buffer;
BOOL rebuildBuffer;
}
@property (nonatomic, assign) NSUInteger frequency;
@property (nonatomic, assign) CGFloat dB;
- (void)play;
- (void)pause;
@end
@implementation Tone
@synthesize dB=_dB,frequency=_frequency;
void handleBuffer(void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer);
#pragma mark - Initialization and deallocation -
- (id)init
{
if ((self=[super init])) {
_dB=0.;
_frequency=440;
rebuildBuffer=YES;
// TO DO: handle AudioQueueXYZ's failures!!
// create a descriptor containing a LPCM, mono, float format
AudioStreamBasicDescription desc;
desc.mSampleRate=TONE_SAMPLERATE;
desc.mFormatID=kAudioFormatLinearPCM;
desc.mFormatFlags=kLinearPCMFormatFlagIsFloat;
desc.mBytesPerPacket=sizeof(float);
desc.mFramesPerPacket=1;
desc.mBytesPerFrame=sizeof(float);
desc.mChannelsPerFrame=1;
desc.mBitsPerChannel=8*sizeof(float);
// create a new queue
AudioQueueNewOutput(&desc,
&handleBuffer,
self,
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes,
0,
&queue);
// and its buffer, ready to hold 1" of data
AudioQueueAllocateBuffer(queue,
sizeof(float)*TONE_SAMPLERATE,
&buffer);
// create the buffer and enqueue it
handleBuffer(self, queue, buffer);
}
return self;
}
- (void)dealloc
{
AudioQueueStop(queue, YES);
AudioQueueFreeBuffer(queue, buffer);
AudioQueueDispose(queue, YES);
[super dealloc];
}
#pragma mark - Main function -
void handleBuffer(void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer) {
// this function takes care of building the buffer and enqueuing it.
// cast inUserData type to Tone
Tone *tone=(Tone *)inUserData;
// check if the buffer must be rebuilt
if (tone->rebuildBuffer) {
// precompute some useful qtys
float *data=inBuffer->mAudioData;
NSUInteger max=inBuffer->mAudioDataBytesCapacity/sizeof(float);
// multiplying the argument by 2pi changes the period of the cosine
// function to 1s (instead of 2pi). then we must divide by the sample
// rate to get TONE_SAMPLERATE samples in one period.
CGFloat unit=2.*M_PI/TONE_SAMPLERATE;
// this is the amplitude converted from dB to a linear scale
CGFloat amplitude=pow(10., tone.dB*.05);
// loop and simply set data[i] to the value of cos(...)
for (NSUInteger i=0; i<max; ++i)
data[i]=(float)(amplitude*cos(unit*(CGFloat)(tone.frequency*i)));
// inform the queue that we have filled the buffer
inBuffer->mAudioDataByteSize=sizeof(float)*max;
// and set flag
tone->rebuildBuffer=NO;
}
// reenqueue the buffer
AudioQueueEnqueueBuffer(inAQ,
inBuffer,
0,
NULL);
/* TO DO: the transition between two adjacent buffers (the same one actually)
generates a "tick", even if the adjacent buffers represent a continuous signal.
maybe using two buffers instead of one would fix it.
*/
}
#pragma - Properties and methods -
- (void)play
{
// generate an AudioTimeStamp with "0" simply!
// (copied from FillOutAudioTimeStampWithSampleTime)
AudioTimeStamp time;
time.mSampleTime=0.;
time.mRateScalar=0.;
time.mWordClockTime=0.;
memset(&time.mSMPTETime, 0, sizeof(SMPTETime));
time.mFlags = kAudioTimeStampSampleTimeValid;
// TO DO: maybe it could be useful to check AudioQueueStart's return value
AudioQueueStart(queue, &time);
}
- (void)pause
{
// TO DO: maybe it could be useful to check AudioQueuePause's return value
AudioQueuePause(queue);
}
- (void)setFrequency:(NSUInteger)frequency
{
if (_frequency!=frequency) {
_frequency=frequency;
// we need to update the buffer (as soon as it stops playing)
rebuildBuffer=YES;
}
}
- (void)setDB:(CGFloat)dB
{
if (dB!=_dB) {
_dB=dB;
// we need to update the buffer (as soon as it stops playing)
rebuildBuffer=YES;
}
}
@end
的類生成在給定整頻率的餘弦波形的振盪(振幅* COS(2PI *頻率* t)的);整個工作由void handleBuffer(...)
完成,使用帶有線性PCM,單聲道,float @ 44.1kHz格式的AudioQueue。爲了改變信號形狀,你可以改變那條線。例如,下面的代碼將產生一個方波:
float x = fmodf(unit*(CGFloat)(tone.frequency*i), 2 * M_PI);
data[i] = amplitude * (x > M_PI ? -1.0 : 1.0);
對於浮點頻率,則應考慮不存在necessarely在音頻數據的二分之一振盪的整數倍,所以信號表示在兩個緩衝區之間的連接處是不連續的,併產生一個奇怪的「嘀嗒」。例如,您可以設置較少的採樣點,以便結點處於信號週期的末尾。
- 正如Paul R指出的那樣,您應該首先校準硬件,以獲得您在實施中設置的值與設備產生的聲音之間的可靠轉換。實際上,此代碼中生成的浮點採樣範圍爲-1到1,因此我只是將幅度值轉換爲dB(20 * log_10(幅度))。
- 查看實施中的其他詳細信息以及「已知限制」(所有這些「待辦事項」)的註釋。蘋果在其參考文獻中詳細記錄了所使用的功能。
分貝(dB)用於表示兩個量值之間的*比率*。你可能意思是'dB SPL'(dB聲壓級),當人們談論聲音以分貝爲單位有多大時,這通常意味着什麼。要生成具有給定dB SPL振幅的聲音,雖然您需要能夠以某種方式校準硬件。 –
我也需要相同的.....必須創建一個嗶嗶頻率和分貝....我正在尋找.. –