Аудио - Сборка / генерация и воспроизведение чистого сигнала - PullRequest
4 голосов
/ 30 октября 2011

РЕДАКТИРОВАТЬ после ответа Бена

Я пытаюсь сделать что-то, что должно быть очень легко для кого-то, кто используется с обработкой сигналов, но это доставляет мне головную боль. Я просто пытаюсь сгенерировать звуковой сигнал, который будет воспроизводиться произвольное количество секунд, может быть меньше или больше секунды (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 герц (используя скорректированную длительность курса). Тем не менее, для целей тестирования, если я использую 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 frames. Поэтому я попытался заменить

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

Для 440 Гц, частота дискретизации 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

Для 440 Гц, частота дискретизации 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

Для 440 Гц, частота дискретизации 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

EDIT

Я записал сгенерированный звуковой образец (440 Гц, 444100 сэмплов, 0,1 секунды) в файл и открыл его с помощью звукового редактора. Вырезайте и вставляйте звук много раз, чтобы сделать его более длинным: он воспроизводится без щелчка. Один и тот же образец звука, воспроизводимый через AVAudioPlayer, генерирует щелчки в конце каждого образца. Так что, похоже, проблема в AVAudioPlayer, по причине, которую я не понимаю, потому что только некоторые конкретные значения генерируют эти клики.

EDIT

Я использовал сгенерированный файл wav и заставил его проигрываться с помощью AVAudioPlayer с циклом: clicks
Я использовал тот же файл и заставил его проигрывать цикл с OpenAL, используя пользовательскую библиотеку: больше никаких кликов. Проблема в том, что OpenAL действительно кошмар для понимания и приведет к полной перезаписи моей звуковой части, просто для этого плохого звука.

Проблема, по-видимому, заключается в использовании AVAudioPlayer. Если у вас было решение заставить его работать, это сэкономит мне время.

Ответы [ 4 ]

4 голосов
/ 30 октября 2011

Выбранная вами частота 200 Гц не является целым числом выборок при 44,1 кГц.Если есть 44100 выборок / с / 200 циклов / с, вы получите 220,5 выборок / цикл.Таким образом, в любое время nbWavesNeeded не является даже (чтобы исключить половину выборки) вашим adjustedDuration при переводе в frames имеет небольшую ошибку округления, которая приводит к появлению всплывающего окна.

(После вашего редактирования на 440 Гц проблема усугубляется, потому что 44100/440 имеет наибольший общий множитель)

Насколько я понял принцип, частота волны - это сколько вверх и внизволны есть в секунду.длительность ... длительность, а sampleRate - это количество срезов за одну секунду.Так что, если я нарежу волну на 1, 10, 50 или 1000 частей, это всегда одна и та же волна, только менее точная.

Это в основном правильно.Таким образом, в hertz = 440 есть «440 восходящих и нисходящих волн в секунду», а при sampleRate = 44100 ваша секунда делится на 44100 срезов.Сколько срезов занимает одна «волна вверх и вниз»?1/440-я секунда, или 1/440-я из ваших 44100 срезов, или 44100 / 440, что составляет 100.2272727272... Так что если frames == 100.22727272.., тогда точный конец "восходящей волны вниз" будет соответствовать точному концу вашего rawSound.Но frames является целым числом, поэтому вы останавливаетесь на frames = 100, поэтому вы сократили волну.Когда звуковой проигрыватель возвращается к 0, он действительно хочет вернуться к 0.2272727..., но, конечно, не может.Вы слышите это как поп.

1 голос
/ 30 октября 2011

Способ генерации чистого непрерывного тона в iOS состоит не в том, чтобы использовать AVAudioPlayer и зависеть от него для правильной конкатенации аудиофрагментов, а в том, чтобы использовать API аудио-очереди или аудиоустройство RemoteIO и контролировать непрерывность передачи звука в буфере обратного вызова самостоятельно.

0 голосов
/ 31 октября 2011

Просматривая ваши правки и образцы данных, я убедительно убежден, что вы избегаете ловушек, которые я описал в моем другом ответе, с конкретными значениями, которые вы выбрали.

Позвольте мне предложить альтернативу: AVAudioPlayer занимаетчересстрочные стерео сэмплы (потому что numberOfChannels равно 2), и когда вы представляете четное количество сэмплов, вы слышите два тона (один очень немного не в фазе с другим) с удвоенной предполагаемой частотой.Когда вы представляете нечетное число (как в вашем последнем примере), отсутствует один образец для одного канала, что приводит к появлению всплывающих окон.

Это дикое предположение, потому что я не разработчик iOS и могуНе понятно, почему numberOfChannels только для чтения, а не для чтения-записи.

0 голосов
/ 31 октября 2011

В общем случае к любому синтезированному звуку, который вы хотите воспроизвести, необходимо применить линейное изменение смещения (aka атака и затухание ), иначе вы получите переходные процессы в началеконец звука, который может быть слышен как щелчки.

Простого линейного линейного изменения в течение периода в несколько мс обычно достаточно для устранения этого, хотя обычно предпочтительнее более плавная форма, такая как экспоненциальный или приподнятый косинус.

Дополнительным бонусом является то, что вам не нужно следить за тем, чтобы ваша форма волны начиналась и заканчивалась в нуле, поскольку об этом заботятся функции начала и смещения.

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