Подтвердить что ты не робот

Запись музыкальных нот в wav файл

Мне интересно, как записывать музыкальные ноты (например, A, B, С# и т.д.) или аккорды (одновременно несколько заметок) и записывать их в wav файл.

Из того, что я понимаю, каждая нота имеет определенную частоту, связанную с ней (для идеального шага) - например, A4 (A выше среднего C) составляет 440 Гц (полный список 2/3 пути вниз Эта страница).

Если мое понимание верное, этот шаг находится в частотной области, и для этого требуется обратное быстрое преобразование Фурье, чтобы сгенерировать эквивалент временной области?

Что я хочу знать:

  • Как работают аккорды? Являются ли они средними для полей?
  • Как длительность воспроизведения каждой записанной ноты, когда содержимое wav файла является формой волны?
  • Как результат нескольких заметок является обратным FFT'd, преобразованным в массив байтов, которые составляют данные в wav файле?
  • любую другую соответствующую информацию, относящуюся к этому.

Спасибо за любую помощь, которую вы можете дать. Если вы даете примеры кода, я использую С#, а код, который я использую для создания wav файлов, выглядит следующим образом:

int channels = 1;
int bitsPerSample = 8;
//WaveFile is custom class to create a wav file.
WaveFile file = new WaveFile(channels, bitsPerSample, 11025);

int seconds = 60;
int samples = 11025 * seconds; //Create x seconds of audio

// Sound Data Size = Number Of Channels * Bits Per Sample * Samples

byte[] data = new byte[channels * bitsPerSample/8 * samples];

//Creates a Constant Sound
for(int i = 0; i < data.Length; i++)
{
    data[i] = (byte)(256 * Math.Sin(i));
}
file.SetData(data, samples);

Это создает (как-то) постоянный звук, но я не совсем понимаю, как код коррелирует с результатом.

4b9b3361

Ответ 1

Вы на правильном пути.

Посмотрим на ваш пример:

for(int i = 0; i < data.Length; i++)
  data[i] = (byte)(256 * Math.Sin(i));

ОК, у вас есть 11025 выборок в секунду. У вас есть образцы за 60 секунд. Каждый образец представляет собой число от 0 до 255, что представляет собой небольшое изменение давления воздуха в точке в пространстве в данный момент времени.

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

for(int i = 0; i < data.Length; i++)
  data[i] = (byte)(128 + 127 * Math.Sin(i));

Теперь у нас есть плавно изменяющиеся данные, которые идут от 1 до 255, поэтому мы находимся в диапазоне байтов.

Попробуйте это и посмотрите, как это звучит. Это должно звучать много "более гладко".

Человеческое ухо обнаруживает невероятно крошечные изменения давления воздуха. Если эти изменения образуют повторяющийся паттерн, тогда частота повторения рисунка интерпретируется улиткой в ​​ухе как особый тон. Размер изменения давления интерпретируется как объем.

Ваш сигнал длится шестьдесят секунд. Изменение происходит от наименьшего изменения, 1 до самого большого изменения, 255. Где пики? То есть, где образец достигает значения 255 или близко к нему?

Ну, синус равен 1 при π/2, 5π/2, 9π/2, 13π/2 и т.д. Таким образом, пики - это когда я близок к одному из них. То есть, 2, 8, 14, 20,...

Насколько далеко друг от друга это время? Каждый образец составляет 1/11025th секунды, поэтому пики составляют около 2π/11025 = около 570 микросекунд между каждым пиком. Сколько пиков есть в секунду? 11025/2π = 1755 Гц. (Герц - мера частоты, количество пиков в секунду). 1760 Гц - две октавы выше A 440, поэтому это слегка плоский тон A.

Как работают аккорды? Являются ли они средними для полей?

Нет. Аккорд, который составляет A440 и октава выше, A880 не эквивалентен 660 Гц. Вы не усредняете поле. Вы суммируете форму волны.

Подумайте о давлении воздуха. Если у вас есть один вибрационный источник, который нагнетает давление вверх и вниз 440 раз в секунду, а другой, который нагнетает давление вверх и вниз 880 раз в секунду, сеть не такая же, как вибрация 660 раз в секунду. Он равен сумме давлений в любой момент времени. Помните, что весь WAV файл: большой список изменений давления воздуха.

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

for(int i = 0; i < data.Length; i++)
  data[i] = (byte)(128 + 127 * Math.Sin(i/2.0)); 

Обратите внимание, что это должно быть 2.0, а не 2. Мы не хотим округлять целые числа! 2.0 сообщает компилятору, что вы хотите получить результат с плавающей точкой, а не целыми числами.

Если вы это сделаете, вы получите половинки так же часто: в я = 4, 16, 28... и, следовательно, тон будет на октаву ниже. (Каждая октава понижает половину частоты, каждая октава удваивает ее.)

Попробуйте это и посмотрите, как вы получите тот же тон, на октаву ниже.

Теперь добавьте их вместе.

for(int i = 0; i < data.Length; i++)
  data[i] = (byte)(128 + 127 * Math.Sin(i)) + 
            (byte)(128 + 127 * Math.Sin(i/2.0)); 

Возможно, это звучало как дерьмо. Что случилось? Мы снова переполнили; сумма была больше 256 во многих точках. Уменьшите объем обеих волн:

for(int i = 0; i < data.Length; i++)
  data[i] = (byte)(128 + (63 * Math.Sin(i/2.0) + 63 * Math.Sin(i))); 

Лучше. "63 sin x + 63 sin y" находится между -126 и +126, поэтому он не может переполнять байты.

(Таким образом, существует среднее значение: мы в основном принимаем среднее значение вклада в давление каждого тона, а не среднее из частот.)

Если вы играете, вы должны одновременно получать оба тона, одну октаву выше, чем другую.

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

  • 128 находится на полпути между низким давлением (0) и высоким давлением (255).
  • объем тона - максимальное давление, достигаемое волной
  • тон - это синусоидальная волна заданной частоты.
  • частота в Гц - это частота выборки (11025), деленная на 2π

Итак, пусть все вместе:

double sampleFrequency = 11025.0;
double multiplier = 2.0 * Math.PI / sampleFrequency;
int volume = 20;

// initialize the data to "flat", no change in pressure, in the middle:
for(int i = 0; i < data.Length; i++)
  data[i] = 128;

// Add on a change in pressure equal to A440:
for(int i = 0; i < data.Length; i++)
  data[i] = (byte)(data[i] + volume * Math.Sin(i * multiplier * 440.0))); 

// Add on a change in pressure equal to A880:

for(int i = 0; i < data.Length; i++)
  data[i] = (byte)(data[i] + volume * Math.Sin(i * multiplier * 880.0))); 

И вот вы идете; теперь вы можете генерировать любой тон, который вы хотите, с любой частотой и громкостью. Чтобы сделать аккорд, добавьте их вместе, убедившись, что вы не слишком громко и переполняете байт.

Откуда вы знаете частоту ноты, отличную от A220, A440, A880 и т.д.? Каждый полутонус умножает предыдущую частоту на 12-й корень из 2. Итак, вычислите 12-й корень из 2, умножьте на 440 и что A #. Умножьте A # на 12 корней из 2, что B. B раз 12-й корень из 2 является C, затем С# и т.д. Сделайте это 12 раз, и потому что это 12-й корень из 2, вы получите 880, в два раза больше, чем вы начали.

Как длительность воспроизведения каждой записанной ноты, когда содержимое wav файла является формой волны?

Просто заполните пробел, где звучит тон. Предположим, вы хотите играть в формате A440 в течение 30 секунд, а затем A880 в течение 30 секунд:

// initialize the data to "flat", no change in pressure, in the middle:
for(int i = 0; i < data.Length; i++)
  data[i] = 128;

// Add on a change in pressure equal to A440 for 30 seconds:
for(int i = 0; i < data.Length / 2; i++)
  data[i] = (data[i] + volume * Math.Sin(i * multiplier * 440.0))); 

// Add on a change in pressure equal to A880 for the other 30 seconds:

for(int i = data.Length / 2; i < data.Length; i++)
  data[i] = (byte)(data[i] + volume * Math.Sin(i * multiplier * 880.0))); 

как результат нескольких заметок, являющихся обратными FFT'd, преобразованными в массив байтов, которые составляют данные в wav файле?

Обратный FFT только строит синусоидальные волны и добавляет их вместе, как и мы здесь. Это все!

любую другую соответствующую информацию, относящуюся к этому?

См. мои статьи по этому вопросу.

http://blogs.msdn.com/b/ericlippert/archive/tags/music/

Части один-три объясняют, почему у пианино есть двенадцать нот на октаву.

Часть четвертая относится к вашему вопросу; что мы создаем WAV файл с нуля.

Обратите внимание, что в моем примере я использую 44100 выборок в секунду, а не 11025, и я использую 16-разрядные образцы, которые варьируются от -16000 до +16000 вместо 8-битных выборок, которые варьируются от 0 до 255. Но помимо этих детали, в основном такие же, как ваши.

Я бы рекомендовал перейти к более высокой скорости передачи, если вы собираетесь делать какой-либо сложный сигнал; 8 бит с частотой 11K в секунду будут звучать ужасно для сложных осциллограмм. 16 бит на выборку с 44 тыс. Выборок в секунду - это качество CD.

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

Часть пятая дает интересный пример слуховой иллюзии.

Кроме того, попробуйте посмотреть ваши формы волны с визуализацией "scope" в Windows Media Player. Это даст вам хорошее представление о том, что происходит на самом деле.

UPDATE:

Я заметил, что при добавлении двух заметок вы можете получить шум, возникающий из-за того, что переход между двумя формами сигналов слишком резкий (например, заканчивается в верхней части одного и начинается в нижней части следующего), Как можно преодолеть эту проблему?

Отличный последующий вопрос.

Существенно, что здесь происходит, происходит мгновенный переход от (скажем) высокого давления к низкому давлению, который слышен как "поп". Есть несколько способов справиться с этим.

Техника 1: Фазовый сдвиг

Один из способов - "сдвиг фазы" последующего тона на небольшую величину, чтобы разница между начальным значением последующего тона и конечным значением предыдущего тона. Вы можете добавить термин фазового сдвига следующим образом:

  data[i] = (data[i] + volume * Math.Sin(phaseshift + i * multiplier * 440.0))); 

Если фазовый сдвиг равен нулю, очевидно, что это не изменяется. Фазовый сдвиг 2π (или любое четное кратное π) также не изменяется, так как sin имеет период 2π. Каждое значение между 0 и 2π сдвигается, когда тон "начинается" немного дальше вдоль волны.

Выработка того, что правильный сдвиг фаз может быть немного сложнее. Если вы читаете мои статьи о создании "непрерывного нисходящего" оттенка иллюзии Шепарда, вы увидите, что я использовал простое исчисление, чтобы убедиться, что все постоянно меняется без каких-либо всплесков. Вы можете использовать похожие методы, чтобы выяснить, что означает правильный сдвиг, чтобы исчезнуть поп.

Я пытаюсь выяснить, как создать значение фазового сдвига. Является ли "ArcSin (((первый образец данных новой заметки) - (последний образец данных предыдущей заметки))/noteVolume)" правильно?

Ну, первое, что нужно понять, это то, что не может быть "правильного значения". Если конечная нота очень громкая и заканчивается на пике, а начальная нота очень тихая, в новом тоне не может быть точки, которая соответствует значению старого тона.

Предполагая, что есть решение, что это такое? У вас есть окончательный образец, назовите его y, и вы хотите найти фазовый сдвиг x такой, что

y = v * sin(x + i * freq)

когда я равно нулю. Так что

x = arcsin(y / v)

Однако, это может быть не совсем правильно! Предположим, что у вас есть

sine wave 1

и вы хотите добавить

sine wave 2

Есть два возможных фазовых сдвига:

sine wave 3

и

Sine wave 4

Возьмите дикую догадку о том, какой из них звучит лучше.: -)

Выяснение того, находитесь ли вы на "upstroke" или "downstroke" волны, может быть немного сложнее. Если вы не хотите разрабатывать настоящую математику, вы можете сделать несколько простых эвристик, например: "изменился ли знак различия между последовательными точками данных при переходе?"

Техника 2: конверт ADSR

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

То, что вы хотите сделать, состоит из четырех разных разделов для каждой заметки, называемых атакой, распада, сустейна и освобождения. Объем ноты, воспроизводимой на инструменте, можно моделировать следующим образом:

     /\
    /  \__________
   /              \
  /                \
   A  D   S       R

Объем начинается с нуля. Затем происходит атака: звук быстро поднимается до максимального уровня. Затем он немного ослабляется до уровня сустейна. Затем он остается на этом уровне, возможно, медленно снижается, пока играет нота, а затем возвращается к нулю.

Если вы это сделаете, тогда нет поп-музыки, потому что начало и конец каждой ноты находятся на нулевой громкости. Релиз гарантирует это.

Различные инструменты имеют разные "конверты". Например, орган трубы имеет невероятно короткую атаку, разложение и освобождение; это все сущее, а сустейн бесконечен. Ваш существующий код похож на орган трубы. Сравните, скажем, с фортепиано. Опять же, короткая атака, короткий спад, короткий выпуск, но звук постепенно становится тише во время сустейна.

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

Ответ 2

Вы на правильном пути.:)

Аудиосигнал

Вам не нужно делать обратный FFT (вы могли бы, но вам нужно было бы найти lib для него или реализовать его, а также генерировать в качестве входного сигнала сигнал). Намного легче получить результат, ожидаемый от этого IFFT, который является синусоидальным сигналом с заданной частотой.

Аргумент синуса зависит как от создаваемой ноты, так и от частоты дискретизации генерируемого вами волнового файла (часто равного 44100 Гц, в вашем примере вы используете 11025 Гц).

Для тона 1 Гц вам необходимо иметь синусоидальный сигнал с одним периодом, равным одной секунде. С частотой 44100 Гц в секунду имеется 44100 выборок в секунду, что означает, что нам нужен синусоидальный сигнал с одним периодом, равным 44100 образцам. Поскольку период синуса равен Tau (2 * Pi), получаем:

sin(44100*f) = sin(tau)
44100*f = tau
f = tau / 44100 = 2*pi / 44100

Для 440 Гц получаем:

sin(44100*f) = sin(440*tau)
44100*f = 440*tau
f = 440 * tau / 44100 = 440 * 2 * pi / 44100

В С# это будет примерно так:

double toneFreq = 440d;
double f = toneFreq * 2d * Math.PI / 44100d;
for (int i = 0; i<data.Length; i++)
    data[i] = (byte)(128 + 127*Math.Sin(f*i));

ПРИМЕЧАНИЕ. Я не проверял это, чтобы проверить правильность кода. Я постараюсь сделать это и исправить любые ошибки. Обновление: Я обновил код до того, что работает. Извините за повреждение ушей; -)

Аккорды

Аккорды - это комбинация заметок (см., например, Малый аккорд в Википедии). Таким образом, сигнал будет комбинацией (суммой) синусов с разными частотами.

Чистые тона

Эти тона и аккорды не кажутся естественными, потому что традиционные инструменты не воспроизводят одночастотные тоны. Вместо этого, когда вы играете на А4, существует широкое распределение частот с концентрацией около 440 Гц. См. Например Timbre.

Ответ 3

Никто еще не упомянул алгоритм Karpus Strong plucked string.

Karplus-Строгий синтез строк Это чрезвычайно простой способ генерации реалистичного вырезанного струнного звука. Я написал полифонические музыкальные инструменты/MIDI-плееры в реальном времени, используя это.

Вы делаете это так:

Во-первых, какую частоту вы хотите моделировать? Пусть говорят, что шаг концерта A = 440 Гц

Предположим, что частота дискретизации составляет 44,1 кГц, то есть 44100/440 = 100,25 выборки на длину волны.

Позвольте округлить это до ближайшего целого числа: 100 и создать круговую длину буфера 100.

Таким образом, он будет удерживать одну стоячую волну с частотой ~ 440 Гц (обратите внимание, что это не точно, есть способы обойти это).

Заполните его случайным стативом между -1 и +1 и:

DECAY = 0.99
while( n < 99999 )
    outbuf[n++] = buf[k]

    newVal = DECAY  *  ( buf[k] + buf_prev ) / 2

    buf_prev = buf[k]
    buf[k] = newVal

    k = (k+1) % 100

Это удивительный алгоритм, потому что он настолько прост и генерирует суперзвук.

Лучший способ понять, что происходит, - понять, что случайный статический во временной области белый шум; случайный статический в частотной области. Вы можете представить это как совокупность многих волн различной (случайной) частоты.

Частоты, близкие к 440 Гц (или 2 * 440 Гц, 3 * 440 Гц и т.д.), создадут конструктивную интерференцию с самим собой, поскольку они проходят вокруг кольца снова и снова. Поэтому они будут сохранены. Другие частоты деструктивно вмешиваются в себя.

Кроме того, усреднение действует как фильтр нижних частот - представьте, что ваша последовательность равна +1 -1 +1 -1 +1 -1, если вы усредняете пары, то каждое среднее значение получается как 0. но если у вас медленнее волна равна 0 0,2 0,3 0,33 0,3 0,2... тогда усреднение все еще приводит к волне. Чем дольше волна, тем больше ее энергия сохраняется - т.е. Усреднение вызывает меньшее затухание.

Таким образом, усреднение можно рассматривать как очень простой фильтр нижних частот.

Конечно, есть сложности, необходимость выбора целочисленной длины буфера заставляет квантовать возможные частоты, что становится заметным к вершине фортепиано. Все преодолено, но оно становится тяжелым!

Ссылки:

Вкусный макс /MSP Tutorial 1: Karplus-Strong

Алгоритм Karplus-Strong

JOS, насколько я могу видеть, является ведущим мировым авторитетом в области синтеза синтетического тона, все дороги вернулись к его веб-сайту. Но будьте осторожны, он становится очень сложным и требует математики на уровне университета.