音频 - 构建/生成并播放纯波形

时间:2011-10-30 02:37:31

标签: iphone cocoa-touch math audio signal-processing

按照Ben的回答编辑

我正在努力为那些用于信号处理的人制作一些非常容易的东西,但这让我很头疼。我只是试图产生一个可以播放任意秒数,可能更少或超过一秒钟(0.1秒,0.88秒,1.2秒......)的波浪声。

为了产生波浪声,我正在使用这个众所周知的方法:

+ (NSData*) WAVSoundForFrequency:(float)frequency duration:(float)seconds sampleRate:(unsigned int)sampleRate gain:(float)gain
{
    int frames = seconds * sampleRate;
    float* rawSound = (float*)malloc(frames*sizeof(float));
    if (rawSound == NULL) return nil;

    for (int i = 0; i < frames; i++)
      rawSound[i] = gain * sinf(i*2*M_PI*frequency/sampleRate);

    // converting to raw sound and returning the whole thing
}

这称为basicaly:

AVAudioPlayer* player = [self.audioPlayerManager buildSoundFrequency:200 duration:0.18 sampleRate:44100 gain:1.0];
player.volume = 1.0;
player.numberOfLoops = -1;
[player play];

问题在于,对于这些参数,波形似乎不会在最后完成,因此它会产生每个循环都能听到的咔嗒声。但是如果持续时间和200赫兹使用0.5秒或1.0秒(当然使用adjustDuration),则不会点击。仍然出于测试目的,如果我使用400赫兹或440赫兹而不是200赫兹,我现在点击0.5s。

请注意,循环仅用于测试并查找是否有点击。最后,声音应仅在所需的持续时间内播放。

我猜测那是因为持续时间不是波周期的整数倍,所以我调整了这样的调用,将所需的持续时间调整为最接近的持续时间,该持续时间是一个周期的倍数想要的频率:

float wantedDuration = 0.18;
float hertz = 200;
int wantedSampleRate = 44100;

// Adjusting wanted duration so the duration contains an entiere number of waves
float oneWaveDurationInSeconds = 1.0/hertz;
int nbWavesNeeded = roundf(wantedDuration/oneWaveDurationInSeconds);
float adjustedDuration = nbWavesNeeded * oneWaveDurationInSeconds;

// Adjusting sample rate so one wave takes an entiere number of samples
float oneSampleDuration = 1.0/wantedSampleRate;

int adjustedSamplerate = wantedSampleRate;
while (YES) {
    oneSampleDuration = 1.0/adjustedSamplerate;
    if (roundf(oneWaveDurationInSeconds/oneSampleDuration) == oneWaveDurationInSeconds/oneSampleDuration) break;
    adjustedSamplerate++;
    NSLog(@"%d", adjustedSamplerate);
}

// Debug
float nbSamplesForOneWave = oneWaveDurationInSeconds / (1.0/adjustedSamplerate);
NSLog(@"nbSamplesForOneWave : %f", nbSamplesForOneWave);

// Execute
MyAudioPlayer* player = [self.manager preloadSoundFrequency:hertz duration:adjustedDuration sampleRate:adjustedSamplerate gain:1.0 
                                                 identifier:@"ii" category:@"Radar"];
player.volume = 1.0;
player.numberOfLoops = -1;
[player play];

但仍有点击。

我被告知问题可能是采样率。但我真的不明白为什么。据我所知,采样率是一秒钟内定义的样本数。所以对我而言,它不依赖于持续时间和频率 而且......为什么我不应该有一个0.18s的声音,44100样品质量......

但无论如何......我想象一下,如果我在一秒内采样44100点,要求持续时间0.18应该会产生44100 * 0.18个样本。这是int frames代表的数字。所以我试图替换

      rawSound[i] = gain * sinf(i*2*M_PI*frequency/sampleRate);

      rawSound[i] = gain * sinf(i*2*M_PI*frequency/frames);

这不起作用,只是使声音更加尖锐。我仍然不明白为什么。虽然它会是一种质量较差的声音,因为tehre只是更少的样本。

是否有人可以帮助我根据所需的质量和频率为所需的延迟生成(可能是可循环的)波形声音?

我确信声音(:-))很简单,但我没有看到实现这一目标的方法。

我试图让NSLog看到使用过的值(没有Paul's ramps的日志):

    if (i<20 || i > frames-20) NSLog(@"%f", rawSound[i]);

对于440Hz,44100采样率,1.0持续时间(无调整): 没有点击

2011-10-31 01:02:34.110 testAudio[9602:207] 0.000000
2011-10-31 01:02:34.112 testAudio[9602:207] 0.062648
2011-10-31 01:02:34.113 testAudio[9602:207] 0.125051
2011-10-31 01:02:34.114 testAudio[9602:207] 0.186961
2011-10-31 01:02:34.115 testAudio[9602:207] 0.248138
2011-10-31 01:02:34.116 testAudio[9602:207] 0.308339
2011-10-31 01:02:34.116 testAudio[9602:207] 0.367330
2011-10-31 01:02:34.117 testAudio[9602:207] 0.424877
2011-10-31 01:02:34.117 testAudio[9602:207] 0.480755
2011-10-31 01:02:34.118 testAudio[9602:207] 0.534744
2011-10-31 01:02:34.119 testAudio[9602:207] 0.586632
2011-10-31 01:02:34.121 testAudio[9602:207] 0.636216
2011-10-31 01:02:34.121 testAudio[9602:207] 0.683300
2011-10-31 01:02:34.122 testAudio[9602:207] 0.727699
2011-10-31 01:02:34.123 testAudio[9602:207] 0.769240
2011-10-31 01:02:34.123 testAudio[9602:207] 0.807759
2011-10-31 01:02:34.124 testAudio[9602:207] 0.843104
2011-10-31 01:02:34.125 testAudio[9602:207] 0.875137
2011-10-31 01:02:34.126 testAudio[9602:207] 0.903732
2011-10-31 01:02:34.127 testAudio[9602:207] 0.928777
2011-10-31 01:02:34.130 testAudio[9602:207] -0.928790
2011-10-31 01:02:34.130 testAudio[9602:207] -0.903724
2011-10-31 01:02:34.131 testAudio[9602:207] -0.875102
2011-10-31 01:02:34.132 testAudio[9602:207] -0.843167
2011-10-31 01:02:34.132 testAudio[9602:207] -0.807795
2011-10-31 01:02:34.133 testAudio[9602:207] -0.769245
2011-10-31 01:02:34.134 testAudio[9602:207] -0.727667
2011-10-31 01:02:34.135 testAudio[9602:207] -0.683225
2011-10-31 01:02:34.135 testAudio[9602:207] -0.636283
2011-10-31 01:02:34.136 testAudio[9602:207] -0.586658
2011-10-31 01:02:34.137 testAudio[9602:207] -0.534724
2011-10-31 01:02:34.138 testAudio[9602:207] -0.480687
2011-10-31 01:02:34.138 testAudio[9602:207] -0.424978
2011-10-31 01:02:34.139 testAudio[9602:207] -0.367383
2011-10-31 01:02:34.140 testAudio[9602:207] -0.308342
2011-10-31 01:02:34.140 testAudio[9602:207] -0.248087
2011-10-31 01:02:34.141 testAudio[9602:207] -0.186856
2011-10-31 01:02:34.142 testAudio[9602:207] -0.125132
2011-10-31 01:02:34.142 testAudio[9602:207] -0.062676

对于440Hz,44100采样率,0.5持续时间(无调整): 没有点击

2011-10-31 01:04:51.043 testAudio[9714:207] 0.000000
2011-10-31 01:04:51.045 testAudio[9714:207] 0.062648
2011-10-31 01:04:51.047 testAudio[9714:207] 0.125051
2011-10-31 01:04:51.049 testAudio[9714:207] 0.186961
2011-10-31 01:04:51.049 testAudio[9714:207] 0.248138
2011-10-31 01:04:51.050 testAudio[9714:207] 0.308339
2011-10-31 01:04:51.051 testAudio[9714:207] 0.367330
2011-10-31 01:04:51.052 testAudio[9714:207] 0.424877
2011-10-31 01:04:51.053 testAudio[9714:207] 0.480755
2011-10-31 01:04:51.054 testAudio[9714:207] 0.534744
2011-10-31 01:04:51.055 testAudio[9714:207] 0.586632
2011-10-31 01:04:51.055 testAudio[9714:207] 0.636216
2011-10-31 01:04:51.056 testAudio[9714:207] 0.683300
2011-10-31 01:04:51.057 testAudio[9714:207] 0.727699
2011-10-31 01:04:51.059 testAudio[9714:207] 0.769240
2011-10-31 01:04:51.060 testAudio[9714:207] 0.807759
2011-10-31 01:04:51.060 testAudio[9714:207] 0.843104
2011-10-31 01:04:51.061 testAudio[9714:207] 0.875137
2011-10-31 01:04:51.062 testAudio[9714:207] 0.903732
2011-10-31 01:04:51.062 testAudio[9714:207] 0.928777
2011-10-31 01:04:51.064 testAudio[9714:207] -0.928795
2011-10-31 01:04:51.065 testAudio[9714:207] -0.903730
2011-10-31 01:04:51.065 testAudio[9714:207] -0.875109
2011-10-31 01:04:51.066 testAudio[9714:207] -0.843109
2011-10-31 01:04:51.067 testAudio[9714:207] -0.807731
2011-10-31 01:04:51.067 testAudio[9714:207] -0.769253
2011-10-31 01:04:51.068 testAudio[9714:207] -0.727676
2011-10-31 01:04:51.069 testAudio[9714:207] -0.683324
2011-10-31 01:04:51.070 testAudio[9714:207] -0.636199
2011-10-31 01:04:51.070 testAudio[9714:207] -0.586669
2011-10-31 01:04:51.071 testAudio[9714:207] -0.534736
2011-10-31 01:04:51.072 testAudio[9714:207] -0.480806
2011-10-31 01:04:51.072 testAudio[9714:207] -0.424880
2011-10-31 01:04:51.073 testAudio[9714:207] -0.367282
2011-10-31 01:04:51.074 testAudio[9714:207] -0.308355
2011-10-31 01:04:51.074 testAudio[9714:207] -0.248100
2011-10-31 01:04:51.075 testAudio[9714:207] -0.186989
2011-10-31 01:04:51.076 testAudio[9714:207] -0.125025
2011-10-31 01:04:51.077 testAudio[9714:207] -0.062689

对于440Hz,44100采样率,0.25持续时间(无调整): 点击次数

2011-10-31 01:05:25.245 testAudio[9759:207] 0.000000
2011-10-31 01:05:25.247 testAudio[9759:207] 0.062648
2011-10-31 01:05:25.249 testAudio[9759:207] 0.125051
2011-10-31 01:05:25.250 testAudio[9759:207] 0.186961
2011-10-31 01:05:25.251 testAudio[9759:207] 0.248138
2011-10-31 01:05:25.252 testAudio[9759:207] 0.308339
2011-10-31 01:05:25.252 testAudio[9759:207] 0.367330
2011-10-31 01:05:25.253 testAudio[9759:207] 0.424877
2011-10-31 01:05:25.254 testAudio[9759:207] 0.480755
2011-10-31 01:05:25.254 testAudio[9759:207] 0.534744
2011-10-31 01:05:25.255 testAudio[9759:207] 0.586632
2011-10-31 01:05:25.256 testAudio[9759:207] 0.636216
2011-10-31 01:05:25.257 testAudio[9759:207] 0.683300
2011-10-31 01:05:25.257 testAudio[9759:207] 0.727699
2011-10-31 01:05:25.258 testAudio[9759:207] 0.769240
2011-10-31 01:05:25.259 testAudio[9759:207] 0.807759
2011-10-31 01:05:25.260 testAudio[9759:207] 0.843104
2011-10-31 01:05:25.261 testAudio[9759:207] 0.875137
2011-10-31 01:05:25.261 testAudio[9759:207] 0.903732
2011-10-31 01:05:25.262 testAudio[9759:207] 0.928777
2011-10-31 01:05:25.263 testAudio[9759:207] -0.928781
2011-10-31 01:05:25.264 testAudio[9759:207] -0.903727
2011-10-31 01:05:25.264 testAudio[9759:207] -0.875135
2011-10-31 01:05:25.265 testAudio[9759:207] -0.843105
2011-10-31 01:05:25.266 testAudio[9759:207] -0.807763
2011-10-31 01:05:25.267 testAudio[9759:207] -0.769249
2011-10-31 01:05:25.267 testAudio[9759:207] -0.727692
2011-10-31 01:05:25.268 testAudio[9759:207] -0.683296
2011-10-31 01:05:25.269 testAudio[9759:207] -0.636217
2011-10-31 01:05:25.269 testAudio[9759:207] -0.586638
2011-10-31 01:05:25.270 testAudio[9759:207] -0.534756
2011-10-31 01:05:25.271 testAudio[9759:207] -0.480746
2011-10-31 01:05:25.271 testAudio[9759:207] -0.424873
2011-10-31 01:05:25.272 testAudio[9759:207] -0.367332
2011-10-31 01:05:25.273 testAudio[9759:207] -0.308348
2011-10-31 01:05:25.273 testAudio[9759:207] -0.248152
2011-10-31 01:05:25.274 testAudio[9759:207] -0.186952
2011-10-31 01:05:25.275 testAudio[9759:207] -0.125047
2011-10-31 01:05:25.276 testAudio[9759:207] -0.062652

修改

我将生成的声音样本(440Hz,444100采样率,0.1秒)写入文件,然后用声音编辑器将其打开。多次剪切和粘贴声音以产生更长的声音:它无需点击即可播放。通过AVAudioPlayer播放的相同声音样本会在每个样本结束时生成单击。所以问题似乎出现在AVAudioPlayer中,原因我不明白,因为只有一些特定的值会产生这些点击。

修改

我已经使用了wav生成的文件并使用带有循环的AVAudioPlayer播放:点击
我使用了相同的文件,并使用自定义库使用OpenAL循环播放:不再需要点击。问题是OpenAL真的是一个理解的噩梦,并且会导致我的声音部分被完全重写,只是为了那个糟糕的声音。

问题显然是使用AVAudioPlayer。如果你有一个解决方案让它工作,它将节省我几天。

4 个答案:

答案 0 :(得分:4)

您选择的200Hz频率不是44.1kHz的整数采样。如果有44100个样本/秒/ 200个周期/秒,则获得220.5个样本/周期。因此,当nbWavesNeeded 转换为 adjustedDuration时,frames 的任何时候hertz = 440都不均匀(取消一半样本)会产生小的舍入错误,从而产生弹出窗口。

(编辑到440Hz之后,问题更严重,因为44100/440具有更高的最大公因数)

  

据我了解原理,波频率是指一秒钟内有多少次上下波。持续时间是......持续时间,sampleRate是一秒内有多少次减少。因此,如果我在1个,10个,50个或1000个部分切割波形,它总是相同的波形,只是不那么精确。

这基本上是正确的。因此,在sampleRate = 44100处,“一秒钟内有440个上下波浪”,而44100 / 440则将您的第二个波分为44100个切片。一个“上下波”需要多少片? 1/440秒,或44100个切片的1/440,或100.2272727272...frames == 100.22727272..因此,如果rawSound那么“向上波”的确切结束将对应于frames的确切结尾。但是frames = 100是一个整数,所以你停在0.2272727...,这样你就可以缩短你的波浪。当声音播放器循环回0时,它真的想循环到{{1}},但当然不能。你听说这是一个流行音乐。

答案 1 :(得分:1)

在iOS上生成纯连续音的方法是不使用AVAudioPlayer,并依赖它来正确连接音频片段,但要使用音频队列API或RemoteIO音频单元,并控制音频的连续性自己进入回调缓冲区。

答案 2 :(得分:0)

在一般情况下,您想要播放的任何合成声音需要应用起始和偏移斜坡(也称为攻击衰变),否则您会在声音的开头和结尾,可以听到咔嗒声。

在几ms的时间段内的简单线性斜坡通常足以消除这种情况,尽管通常优选更平滑的形状,例如指数或升余弦。

额外的好处是,您不需要确保波形开始和结束为零,因为起始和偏移功能可以解决这个问题。

const int kAttack = (int)(0.005f * sampleRate); // 5 ms attack period (samples)
const int kDecay = (int)(0.010f * sampleRate);  // 10 ms decay period (samples)

for (int i = 0; i < frames; i++)
{
    float a = gain * sinf((float)i * 2.0f * M_PI * frequency / sampleRate);
    if (i < kAttack)                // if in attack (onset) period
    {
        a *= (float)i / kAttack;    // apply linear onset ramp
    }
    else if (i > frames - kDecay)  // if in decay (offset) period
    {
        a *= 1.0f - (float)(i - (frames - kDecay)) / kDecay;   // apply linear offset ramp
    }           

    rawSound[i] = a;
}

答案 3 :(得分:0)

在看到您的编辑和样本数据之后,我有理由相信您正在避免我在其他答案中描述的陷阱,并使用您选择的具体值。

让我建议一个替代方案:AVAudioPlayer采用交错的立体声样本(因为numberOfChannels是2),当你呈现偶数个样本时,你会听到两个音调(一个非常略微异相另一个)预定频率的两倍。当你出现一个奇数时(如你的上一个例子),一个通道缺少一个样本,导致弹出。

这是一个疯狂的猜测,因为我不是iOS开发人员,我无法理解为什么numberOfChannels是只读而不是读写。