Вы на правильном пути.
Давайте рассмотрим ваш пример:
for(int i = 0; i < data.Length; i++)
data[i] = (byte)(256 * Math.Sin(i));
ОК, у вас есть 11025 выборок в секунду. У вас есть образцы на 60 секунд. Каждый образец представляет собой число от 0 до 255, которое представляет собой небольшое изменение давления воздуха в точке пространства в данный момент времени.
Подождите минуту, хотя синус изменяется от -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/11025-ю секунды, поэтому пики составляют примерно 2/11025 = примерно 570 микросекунд между каждым пиком. Сколько пиков в секунду? 11025 / 2π = 1755 Гц. (Герц - это мера частоты; сколько пиков в секунду). 1760 Гц на две октавы выше А 440, так что это слегка ровный тон А.
Как работают аккорды? Являются ли они средним из смол?
Нет. Аккорд А440 и октавы выше, 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 сообщает компилятору, что вы хотите получить результат с плавающей запятой, а не целые числа.
Если вы сделаете это, вы получите пики вдвое реже: при i = 4, 16, 28 ... и, следовательно, тон будет на полную октаву ниже. (Каждая октава вниз делит пополам частоту; каждая октава вверх удваивает it.)
Попробуйте и посмотрите, как вы получите тот же тон, на октаву ниже.
Теперь сложите их вместе.
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, затем 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 преобразованы в массив байтов, которые составляютданные в wav-файле?
Обратное БПФ просто строит синусоиды и складывает их вместе, как мы делаем здесь.Вот и все!
любая другая важная информация, относящаяся к этому?
Смотрите мои статьи на эту тему.
http://blogs.msdn.com/b/ericlippert/archive/tags/music/
Части с первой по третью объясняют, почему у пианино двенадцать нот на октаву.
Часть четвертая относится к вашему вопросу;вот где мы создаем WAV-файл с нуля.
Обратите внимание, что в моем примере я использую 44100 выборок в секунду, а не 11025, и я использую 16-битные выборки с диапазоном от -16000 до +16000 вместо 8битовые выборки, которые варьируются от 0 до 255. Но помимо этих деталей, они в основном такие же, как и у вас.
Я бы порекомендовал перейти на более высокую битовую скорость, если вы собираетесь использовать какой-либо сложный сигнал;8 бит с частотой 11K сэмплов в секунду будут звучать ужасно для сложных сигналов.Качество CD - 16 бит на семпл с частотой дискретизации 44 Кбайт в секунду.
И, честно говоря, намного проще понять математику, если вы делаете это в знаковых шортах, а не в беззнаковых байтах.
В пятой части представлен интересный пример слуховой иллюзии.
Кроме того, попробуйте смотреть свои волновые формы с помощью визуализации «scope» в Windows Media Player.Это даст вам хорошее представление о том, что на самом деле происходит.
ОБНОВЛЕНИЕ:
Я заметил, что при добавлении двух заметок вместе вы можете получить шум,из-за того, что переход между двумя сигналами слишком резкий (например, заканчивается в верхней части одной и начинается в нижней части следующей).Как можно решить эту проблему?
Отличный вопрос для продолжения.
По сути, здесь происходит мгновенный переход от (скажем) высокого давления к низкому давлениюуслышал как "поп".Есть несколько способов справиться с этим.
Техника 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)
, когда i равен нулю.Так что это
x = arcsin(y / v)
Однако , это может быть не совсем правильно!Предположим, у вас есть
и вы хотите добавить
Есть два возможных сдвига фаз :
и
Сделайте дикое предположение, какой из них звучит лучше.: -)
Выяснить, находитесь ли вы на "ход вверх" или "вниз ход" волны, может быть немного сложно.Если вы не хотите заниматься реальной математикой, вы можете сделать несколько простых эвристических операций, например: «Признак различия между последовательными точками данных изменился на переходе?»
Техника 2:Конверт ADSR
Если вы моделируете что-то, что должно звучать как настоящий инструмент, вы можете получить хорошие результаты, изменив громкость следующим образом.
То, что вы хотите сделать, этоесть четыре различных раздела для каждой ноты, называемые атака, распад, сустейн и релиз.Громкость ноты, сыгранной на инструменте, может быть смоделирована следующим образом:
/\
/ \__________
/ \
/ \
A D S R
Громкость начинается с нуля.Затем происходит атака: звук быстро достигает максимальной громкости.Затем он немного разлагается до своего уровня поддержания.Затем он остается на этом уровне, возможно, медленно снижается во время воспроизведения ноты, а затем возвращается к нулю.
Если вы сделаете это, то всплывающего окна не будет, потому что начало и конец каждой ноты равны нулю.объем.Релиз гарантирует это.
Разные инструменты имеют разные «конверты».Трубный орган, например, имеет невероятно короткие атаки, разрушение и высвобождение;это все сустейн, а сустейн бесконечен.Ваш существующий код подобен органу.Сравните с, скажем, пианино.Снова короткая атака, короткое затухание, короткое освобождение, но звук постепенно становится тише во время сустейна.
Секции атаки, затухания и выпуска могут быть очень короткими, слишком короткими, чтобы их можно было услышать, но достаточно длинными, чтобы предотвратить появление.Поэкспериментируйте с изменением громкости во время воспроизведения ноты и посмотрите, что произойдет.