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

发布于 2024-12-12 08:26:17 字数 10036 浏览 0 评论 0原文

按照本的回答进行编辑

我正在尝试制作一些对于使用信号处理的人来说应该非常容易的东西,但这让我头疼。我只是想生成一个可以播放任意秒数的波形声音,可以少于或多于一秒(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
}

这基本上被称为:

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

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

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

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

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.18 秒的声音和 44100 个采样质量...

但无论如何...我想象过,如果我在一秒钟内采样 44100 个点,要求 0.18 的持续时间应该会导致44100*0.18 个样本。这是由 int 帧 表示的数字。所以我尝试

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

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

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

有人可以帮助我以所需的质量和频率生成任何想要的延迟的(可能是可循环的)波形声音吗?

我确信这听起来(:-))很容易,但我看不到实现这一目标的方法。

我尝试放置 NSLog 来查看使用的值(没有保罗斜坡的日志):

    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 的使用。如果你有一个解决方案让它工作,它会节省我的时间。

EDIT following Ben's answer

I'm trying to make something that should be very easy for someone that is used with signal processing, but that gives me headaches. I'm just trying to generate a wave sound that would play an arbitrary number of seconds, could be less, or more than a second (0.1s, 0.88s, 1.2s, ...).

To generate the wave sound, I'm using that well known method :

+ (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
}

That is called basicaly with :

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

The problem is that with those params, the wave seems no to be complete at the end so it generate clicks that can be heard on each loop. But no click if I use 0.5 seconds or 1.0 seconds for duration and 200 hertz (using the adjustedDuration of course). Still for test purpose, if I use 400 hertz or 440 hertz instead of 200, I have now clicks with 0.5s.

Note that the loop is only here for testing and find if there are clicks, or not. At the end, the sound should only play at the desired duration.

I had guessed that that was because of the duration that is not a round multiple of the wave cycle, so I have adjusted the call like this to adjust the wanted duration to the nearest duration that would be a multiple of one cycle at the wanted frequency :

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];

But there is still a click.

I've been told that the problem could be the sample rate. But I really don't understand why. As far as I've understood, the sample rate is the number of samples defined in one second. So for me it's not dependent on the duration nor the frequency.
And... Why shouldn't I have a sound of 0.18s with a 44100 sample quality...

But anyway... I have imagined that if I sample 44100 points in one second, asking for a duration of 0.18 should lead to a 44100*0.18 samples. This is the number represented by int frames. So I have tried to replace

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

with

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

That doesn't work, that just make the sound far more acute. And I still don't understand why. I though it would be a less quality sound, as tehre are just less samples.

Could someone help me to generate that (possibly loopable) wave sound for any wanted delay, on the desired quality and frequency ?

I'm sure that sounds ( :-) ) easy, but I don't see the way to follow to achieve this goal.

I tried to put a NSLog to see the used values (log without Paul's ramps) :

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

For 440Hz, 44100 sample rate, 1.0 duration (no adjustment) :
No click

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

For 440Hz, 44100 sample rate, 0.5 duration (no adjustment) :
No click

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

For 440Hz, 44100 sample rate, 0.25 duration (no adjustment) :
Hard clicks

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

EDIT

I've wroten the generated sound sample (440Hz, 444100 sample rate, 0.1 second) into a file, and opened it with a sound editor. Cut and paste the sound many times to make a longer sound : it plays with no click. The same sound sample played through the AVAudioPlayer generate clicks at the end of each sample. So the problem seems to be in the AVAudioPlayer, for a reason I do not understand, because only some specific values generate those clicks.

EDIT

I've used the wav generated file and made it play with an AVAudioPlayer with a loop : clicks
I've used the same file and made it play with a loop with OpenAL using a custom library : no more clicks. The problem is that OpenAL is really a nightmare to understand and would lead to a compleet rewrite of my sound part, just for that poor sound.

The problem is apparently the use of AVAudioPlayer. If you had a solution to make it work, it will save me days.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

最冷一天 2024-12-19 08:26:18

您选择的 200Hz 频率不是 44.1kHz 处样本的整数个。如果有 44100 个样本/秒/200 个周期/秒,则您将获得 220.5 个样本/周期。因此,任何时候 nbWavesNeeded 不均匀(以取消一半样本),您的 adjustedDuration 当转换为 frames 时产生流行音乐的小舍入误差。

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

据我理解的原理,波频就是一秒钟有多少个上下波。持续时间是...持续时间,而采样率是一秒钟内有多少次剪切。因此,如果我将一个波浪切割成 1、10、50 或 1000 个部分,它总是相同的波浪,只是精度较低。

这基本上是正确的。因此,在 hertz = 440 时,“一秒内有 440 个上下波”,而在 sampleRate = 44100 下,您的第二个波被分为 44100 个切片。一次“上下波”需要切多少片? 1/440 秒,或 44100 个切片的 1/440,或 44100 / 440,即 100.2272727272... 因此,如果 frames == 100.22727272。 . 那么“向上向下波”的确切结束将对应于您的确切结束rawSound。但 frames 是一个整数,因此您在 frames = 100 处停止,因此您已经缩短了波形。当声音播放器循环回到 0 时,它确实想循环到 0.2272727... 但当然不能。你听到的是流行音乐。

Your chosen frequency of 200Hz is not an integral number of samples at 44.1kHz. If there are 44100 samples/sec / 200 cycles/sec you get 220.5 samples/cycle. So any time nbWavesNeeded is not even (to cancel out the half sample) your adjustedDuration when translated into frames has a small roundoff error which produces the pop.

(After your edit to 440Hz the problem is worse because 44100/440 has a higher greatest common factor)

As far as I've understood the principle, the wave frequency is how many up-and-down waves there are in a second. the duration is... the duration, and the sampleRate is how many cuts there are in one second. So if I cut a wave in 1, 10, 50 or 1000 parts, it's always the same wave, just less precise.

That is basically correct. So at hertz = 440 there are "440 up and down waves in a second" and with sampleRate = 44100 your second is divided into 44100 slices. How many slices does one "up and down wave" take? 1/440th of a second, or 1/440th of your 44100 slices, or 44100 / 440 which is 100.2272727272... So if frames == 100.22727272.. then the exact end of an "up down wave" would correspond to the exact end of your rawSound. But frames is an integer, so you stop at frames = 100 so you have cut your wave short. When the sound player loops back to 0 it really wants to loop to 0.2272727... but of course it can't. You hear that as a pop.

茶花眉 2024-12-19 08:26:18

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

The way to generate a pure continuous tone on iOS is to not use AVAudioPlayer, and depend on it to properly concatenate audio fragments, but to use the Audio Queue API or the RemoteIO Audio Unit, and control the continuity of the audio going into the callback buffers yourself.

窗影残 2024-12-19 08:26:18

一般情况下,您想要播放的任何合成声音都需要应用起始和偏移斜坡(又名攻击衰减),否则您会在开始和结束时出现瞬变声音,可能会听到喀哒声。

尽管通常优选指数或升余弦等更平滑的形状,但几毫秒周期内的简单线性斜坡通常足以消除这种情况。

另一个好处是,您不需要确保波形以零开始和结束,因为起始和偏移函数会处理这一点。

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;
}

In the general case any synthesised sound which you want to play needs to have an onset and offset ramp applied (aka attack and decay) otherwise you get transients at the beginning and end of the sound, which may be audible as clicks.

A simple linear ramp over a period of a few ms is usually sufficient to eliminate this, although a smoother shape such as exponential or raised cosine is generally preferred.

An added bonus is that you do not need to ensure that your waveform begins and ends at zero, since the onset and offset function takes care of this.

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;
}
メ斷腸人バ 2024-12-19 08:26:18

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

让我建议一个替代方案:AVAudioPlayer 采用交错的立体声样本(因为 numberOfChannels 为 2),当您提供偶数个样本时,您会听到两种音调(其中一种音调稍微超出了与另一个相位)的频率是预期频率的两倍。当您提供奇数时(如上一个示例),一个通道缺少一个样本,从而导致弹出声音。

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

After seeing your edits and sample data I am reasonably convinced you are avoiding the pitfalls I described in my other answer with the specific values you've chosen.

Let me suggest an alternative: AVAudioPlayer takes interleaved stereo samples (because numberOfChannels is 2) and when you present an even number of samples you hear two tones (one very slightly out of phase with the other) at twice the intended frequency. When you present an odd number (as in your last example) there is one sample missing for one channel which leads to a pop.

This is a wild guess because I'm not an iOS developer and I can't understand why numberOfChannels is read-only instead of read-write.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文