EDIT:最后,我完全按照下面的解释,使用 AVRecorder 来录制语音,使用 openAL 来进行音调转换和播放。效果很好。
我有一个关于录制、修改和播放音频的问题。我之前也问过类似的问题(在 iOS 上实时录制、修改音高和播放音频 https://stackoverflow.com/questions/4393415/iphone-record-modify-pitch-and-play-back-audio-in-real-time)但我现在有更多信息,可以提供一些进一步的建议。
首先,这就是我想要做的(在主线程的单独线程上):
- 监听 iPhone 麦克风
- 检查声音是否大于特定音量
- 如果高于阈值开始记录,例如人们开始说话
- 继续记录,直到音量低于阈值,例如人停止说话
- 修改录制声音的音调。
- 播放声音
我正在考虑使用 AVRecorder 来监听和录制声音,这里有很好的教程:http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/ http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/
我正在考虑使用 openAL 来修改录制音频的音高。
所以我的问题是,我在上面列出的几点中的想法是否正确,我是否遗漏了某些内容,或者是否有更好/更简单的方法来做到这一点。我可以避免混合音频库而只使用 AVFoundation 来改变音高吗?
您可以使用 AVRecorder 或诸如实时 IO 音频单元之类的较低版本。
“体积”的概念相当模糊。您可能想了解计算峰值和 RMS 值之间的差异,并了解如何在给定时间(例如 VU 表使用的 300 毫秒)内对 RMS 值进行积分。
基本上,您将所有值的平方相加。您可以使用 10 * log10f(sqrt(sum/num_samples)) 取平方根并转换为 dBFS,但您可以使用 20 * log10f(sum/num_samples) 一步完成,无需 sqrt。
您需要对积分时间和阈值进行大量调整,以使其按照您想要的方式运行。
对于音调变换,我认为 OpenAL 可以解决这个问题,其背后的技术称为频带限制插值 -https://ccrma.stanford.edu/~jos/resample/Theory_Ideal_Bandlimited_Interpolation.html https://ccrma.stanford.edu/~jos/resample/Theory_Ideal_Bandlimited_Interpolation.html
此示例显示了作为运行平均值的均方根计算。循环缓冲区保留了平方的历史记录,并且无需在每次操作时对平方求和。我还没有运行它,所以将其视为伪代码;)
Example:
class VUMeter
{
protected:
// samples per second
float _sampleRate;
// the integration time in seconds (vu meter is 300ms)
float _integrationTime;
// these maintain a circular buffer which contains
// the 'squares' of the audio samples
int _integrationBufferLength;
float *_integrationBuffer;
float *_integrationBufferEnd;
float *_cursor;
// this is a sort of accumulator to make a running
// average more efficient
float _sum;
public:
VUMeter()
: _sampleRate(48000.0f)
, _integrationTime(0.3f)
, _sum(0.)
{
// create a buffer of values to be integrated
// e.g 300ms @ 48khz is 14400 samples
_integrationBufferLength = (int) (_integrationTime * _sampleRate);
_integrationBuffer = new float[_integrationBufferLength + 1];
bzero(_integrationBuffer, _integrationBufferLength);
// set the pointers for our ciruclar buffer
_integrationBufferEnd = _integrationBuffer + _integrationBufferLength;
_cursor = _integrationBuffer;
}
~VUMeter()
{
delete _integrationBuffer;
}
float getRms(float *audio, int samples)
{
// process the samples
// this part accumulates the 'squares'
for (int i = 0; i < samples; ++i)
{
// get the input sample
float s = audio[i];
// remove the oldest value from the sum
_sum -= *_cursor;
// calculate the square and write it into the buffer
double square = s * s;
*_cursor = square;
// add it to the sum
_sum += square;
// increment the buffer cursor and wrap
++_cursor;
if (_cursor == _integrationBufferEnd)
_cursor = _integrationBuffer;
}
// now calculate the 'root mean' value in db
return 20 * log10f(_sum / _integrationBufferLength);
}
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)