Генератор тона генерирует второй тон

Я пытаюсь сделать простой генератор сигналов для изохронных пульсирующих звуков.

По сути, «beatFrequency» контролирует изменение амплитуды основной частоты (высоты тона).

Это работает довольно хорошо, за исключением того, что для некоторых частот основного тона выше 4-5 кГц есть второй тон, генерируемый с более низкой частотой.
Это не для всех частот, но для довольно многих я определенно слышу второй тон.

Что это может быть? Какой-то резонанс? Я пробовал увеличивать частоту дискретизации, но это ничего не меняет, а использования 44100 должно хватить примерно до 20 кГц, если я правильно понимаю?

Я действительно не могу понять это самостоятельно, так что спасибо всем за помощь!

Вот пример кода с beatFreequency 1 Гц, частотой тона 5000 Гц и частотой дискретизации 44100.

public void playSound() {

double beatFreq = 1;
double pitch = 5000;
int mSampleRate = 44100;

    AudioTrack mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,   mSampleRate,
        AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
        256, AudioTrack.MODE_STREAM);

int loopLength = 2 * this.mSampleRate;

while (isPlaying) {
    double[] mSound = new double[loopLength];

    for (int i = 0; i < loopLength; i = i + 1) {
       mSound[i] = beatFreq*Math.sin((1.0*Math.PI * i/(this.mSampleRate/pitch)));
       mBuffer[i] = (short) (mSound[i] * Short.MAX_VALUE);
    }

    mAudioTrack.play();
    mAudioTrack.write(mBuffer, 0, loopLength);

}
}

Вот (добавлено) изображение частот, когда я играю тон 4734 Гц. И, например, есть довольно большой пик около 1100 Гц, а также многие выше.

введите здесь описание изображения

Код теперь просто использует высоту тона, я удалил частоту ударов:


person Tessmus    schedule 10.06.2018    source источник
comment
Чтобы быстрее получить помощь, опубликуйте минимально воспроизводимый пример или Короткий, автономный, правильный пример.   -  person Andrew Thompson    schedule 10.06.2018


Ответы (2)


В вашем коде вы используете beatFreq*Math.sin((1.0*Math.PI * i/(this.mSampleRate/pitch)));для определения частоты (здесь отсутствует какое-то назначение)

Однако mSampleRate и высота тона являются значениями типа int, что приводит к целочисленному делению вместо двойного деления. В особых случаях для высоты тона и частоты дискретизации это приведет к более низким частотам, чем предполагалось. Для больших значений высоты тона эффект должен ухудшаться.

Попробуйте использовать double вместо int, это должно избавиться от проблемы.

person Holger    schedule 10.06.2018
comment
Да, я забыл поставить в одной строке mSound[i]. Теперь я сделал двойные значения высоты тона и beatFreq, но проблема все еще существует, я определенно слышу, как более низкие тона генерируются параллельно с предполагаемым. - person Tessmus; 10.06.2018
comment
Можете ли вы узнать, является ли более высокий тон, который вы слышите, кратным более низкому второму? Или лучше несколько примеров частот, где это происходит и не происходит? Возможно, это даст больше понимания проблемы. я бы попробовал с моно каналом - person Holger; 11.06.2018
comment
Я не знаю, что такое дополнительный тон, я хотел бы, чтобы я мог его как-то измерить ... Я действительно не могу найти какой-либо закономерности, где я могу его слышать, а не нет, но теперь я также понимаю, что даже когда я не слышу второй тон, сгенерированный тон не правильный, - person Tessmus; 11.06.2018
comment
Поднимаясь вверх по шкале частоты, тон иногда становится ниже, а не выше для некоторых тонов выше 4000 Гц. Например, 4369 звучит выше, чем 5242. А 6335 Гц звучит выше, чем 7305. Для других это не так, 5242 звучит ниже, чем 5971, а 6699 звучит ниже, чем 7427. - person Tessmus; 11.06.2018
comment
Я отредактировал вопрос выше, чтобы включить спектр изображений генерируемых дополнительных частот. - person Tessmus; 12.06.2018
comment
Итак, теперь вычисляется только чистая синусоидальная волна, но генерируются многие другие тона (множества и части основного тона), поскольку я не делаю никаких других модификаций сигнала, кажется, это что-то наследуется в генерации AudioTrack. звука? Можно ли что-то сделать с сигналом после того, как он был сгенерирован? Или лучше сделать что-то другое в расчете сигнала? - person Tessmus; 13.06.2018
comment
Я сомневаюсь, что ответ на вашу проблему связан с генерацией тона. Я думал о том, как андроид обрабатывает тоны, но все равно это не объясняет два разных тона. Две вещи, которые я придумал: 1. AudioFormat.ENCODING_PCM_16BIT: Можете ли вы попробовать установить несжатый формат? Может быть, алгоритм, стоящий за этим, каким-то образом сжимает звук, что приводит к появлению второго тона. 2. Можете ли вы протестировать тот же код на другой системе, отличной от Android (или на другом устройстве Android)? Это исключит проблему Android или звукового движка Android. - person Holger; 14.06.2018
comment
Я попытался установить формат: AudioFormat.ENCODING_PCM_FLOAT, но тогда звук вообще не выходит, не уверен, что мне нужно изменить что-то еще, используя PCM_FLOAT? Я протестировал его на нескольких разных виртуальных устройствах, но на одном и том же компьютере, я еще не пробовал его на реальном устройстве Android, так как по какой-то причине не смог заставить его работать с моей нынешней галактикой s7. - person Tessmus; 15.06.2018
comment
developer.android.com/reference/android/media/AudioFormat Я думаю, проблема в том, что ENCODING_PCM_FLOAT принимает только значения от -1 до 1. Поэтому вы превышаете максимальные/минимальные значения. Но кроме этого, это не похоже на решение. - person Holger; 16.06.2018
comment
Что тогда будет несжатым форматом? Как мне это установить? - person Tessmus; 16.06.2018

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

Да, Java может выполнять амплитудную модуляцию. Например, я сделал сносный звук крикета, взяв тон 4,9 кГц и модулируя громкость на уровне 66 Гц, и придав полученному тону огибающую AR.

В вашем коде переменная beatFreq остается постоянной на протяжении всего цикла for. Разве вы не намерены менять и это со временем?

Я думаю, вы должны одновременно вычислить значение волны beatFreq в своей собственной функции (но также с использованием переменного i) и умножить этот результат (масштабированный в диапазоне от 0 до 1) на значение, вычисленное для более быстрого тона.


** РЕДАКТИРОВАТЬ


Для перемещения избыточных вычислений из внутреннего цикла возможно следующее:

Имейте следующие переменные экземпляра:

private double incr;
private double incrSum;
private final double TWO_PI = Math.PI * 2;

Выполните следующий расчет только один раз в своем конструкторе:

    incr = pitch / audioFmt.getSampleRate();
    incr *= TWO_PI;

Это предполагает, что pitch - это значение, указанное в герцах, а `audioFormat' - это используемый Java AudioFormat. В Android я не знаю, как сохраняется или используется частота дискретизации аудиоформата.

С этим у вас может быть метод, который возвращает следующее двойное значение PCM с помощью очень простого кода:

private double getNextSinePCM()
{
    incrSum += incr;
    if (incrSum > TWO_PI)
    {
        incrSum -= TWO_PI;
    }
    return Math.sin(incrSum);
}

Примечание: не сбрасывайте incrSum на ноль, как вы указали в своем комментарии. Это может привести к разрыву.

Если у вас есть второй тон, он получит свое собственное приращение и текущую сумму. Эти два результата можно затем перемножить, чтобы получить амплитудную модуляцию.

Теперь, что касается вопроса о том, как правильно преобразовать двойное значение PCM, возвращаемое во что-то, что может использовать Android, я не могу дать вам окончательный ответ, поскольку я не использую Android.

Просто комментарий: мне кажется, вы с энтузиазмом относитесь к работе со звуком, но, возможно, самоучка или немного отстаете в базовых приемах программирования. Перемещение избыточных вычислений за пределы цикла является фундаментальным. Как и возможность создавать простые тестовые примеры для проверки предположений и устранения неполадок. По мере того, как вы продвигаетесь вперед, я хочу призвать вас посвятить некоторое время развитию этих основ, а также проявлять интерес к звуку! Вы можете проверить группу чтения кода StackOverflow в качестве ресурса для получения дополнительных советов. Я также самоучка и многому научился там, а также на курсе по чтению кода в JavaRanch под названием «CattleDrive».

person Phil Freihofner    schedule 10.06.2018
comment
Ну, разве это не AM, так как я позволяю одной синусоидальной волне (частоте биений) модулировать амплитуду высоты тона (несущей частоты). Не уверен, что понял все, что вы написали, но я хочу генерировать пульсирующий звук с определенной высотой тона, и я не думаю, что фазовая модуляция сделает это, так что вы думаете, есть ли способ в Java сделать пульсирующий звук с одним бить Freq и один Pitch, или это ограничение в языке Java тогда? - person Tessmus; 10.06.2018
comment
Добавлен новый ответ, основанный на понимании того, что ваша цель - выполнить амплитудную модуляцию. - person Phil Freihofner; 10.06.2018
comment
Идея состоит в том, что частота ударов — это переменная, которой можно управлять из интерфейса приложения, но пока я могу оставить ее постоянной, так как ее тональный тон ведет себя странно. Как и на изображении выше, генерируются несколько тонов, где должен быть только один, как кратные, так и множители основного тона... не уверен, почему это так и как я могу это обойти. - person Tessmus; 11.06.2018
comment
Я взглянул на кольцевую модуляцию в Википедии. Я думаю, что вы, возможно, установили это для себя. В статье говорится, что это возникает, когда два сигнала перемножаются (по сути, свертка). Выход кольцевого модулятора включает суммарный и разностный тоны. en.wikipedia.org/wiki/Ring_modulation Если вы слышите слышимый низкий тон, ваш 1 Тон Гц вполне может быть довольно разной высотой. - person Phil Freihofner; 12.06.2018
comment
Да, я думаю, это то, что происходит ... Я добавил изображение частот, когда я воспроизводю сигнал 4734 Гц, и есть много пиков, например, один на частоте около 1100 Гц, это то, что я слышу ... Но я получить этот эффект сейчас, даже если не перемножаются 2 сигнала, я удалил beatFreq и теперь просто генерирую высоту тона. - person Tessmus; 12.06.2018
comment
Прежде чем идти вперед, нужно иметь возможность произвести простой синус! Проверить, есть ли разрыв между буферами? То, как вы обрабатываете фазу (через переменную i), выглядит проблематично при переходе от буфера к буферу. Если ваша частота не кратна частоте дискретизации, вы, вероятно, получаете большой скачок между последним i и первым i следующего. - person Phil Freihofner; 12.06.2018
comment
Да, это правда, раньше у меня была проблема с клиппированием звука между лупами, но по какой-то причине эта проблема исчезла. У вас есть предложение, как сделать это по-другому, чтобы избежать разрыва между буферами? - person Tessmus; 14.06.2018
comment
Переместите переменные, которые должны обеспечивать непрерывность, за пределы цикла заполнения буфера. Я бы рассмотрел возможность определения приращения (вычисляемого один раз по частоте и частоте дискретизации), которое постепенно добавляется к переменной текущей суммы. (Хороший побочный эффект: не выполнять деление на каждой итерации.) Кроме того, проверьте текущую переменную суммы и, когда она достигнет точки цикла (2PI), вычтите один цикл, таким образом гарантируя, что аргумент вашей функции знака никогда не переполнится. - person Phil Freihofner; 14.06.2018
comment
Это имеет большой смысл! Я пробовал, но не думаю, что сделал это правильно ... в основном, вне цикла я вычислял переменную inc. double inc = шаг/this.mSampleRate; двойная сумма = 0; И затем внутри цикла я добавил: sum = sum + inc; если(сумма › 2.0*Math.PI) сумма = 0; и изменил вычисление на: Math.sin(1.0*Math.PI * sum) Я здесь неправильно думаю? В результате получается странный звук, на низких частотах он сильно обрезается, например, частота дискретизации слишком низкая, высота тона также звучит как более низкая частота, и появляется еще больше множественных обертонов и т. д., чем раньше. - person Tessmus; 14.06.2018