Saw oscillator что это

Обновлено: 14.05.2024

Built on decades of engineering expertise, Microsemi’s precision crystal and surface acoustic wave (SAW) oscillator portfolio delivers precision frequency control and timing solutions for various applications. Products include crystals and crystal oscillators, frequency translators, clock and data recovery products, SAW filters and components used in several markets such as aerospace, communications, data storage, industrial, medical, military, navigation, test and measurement, and instrumentation systems.

We work closely with customers to understand and design systems to meet their specific application requirements, such as low phase noise, high stability, high shock and vibration, low g-sensitivity, low jitter and extended temperature range.

Select any of the product types to find more details, or search our capabilities using the parametric search tab. Products may be filtered by numeric requirements, specific applications and markets, and feature requirements.

Want to speak to an expert? Contact our global support team to speak with a Microsemi timing and synchronization about your specific needs.

Класс Oscillator

Материал этой статьи целиком относится к теме DSP. Мы не будем просто писать весь новый код внутри функции ProcessDoubleReplacing . Вместо этого создадим класс Oscillator , будем вызывать его функции из ProcessDoubleReplacing , и он будет наполнять выходной буфер значениями типа double для создания формы волны. Сначала используем интуитивный подход. Позже, столкнувшись с недостатками этого подхода, мы найдем способ добиться лучшего звучания.

Давайте создадим новый класс. На Mac File → New → File…:


На Windows правым кликом по проекту, Add →Class:


Назовем его Oscillator. Убедитесь, что Oscillator.cpp компилируется. В Xcode в таргете AU нажмите на Build Phases. Щелкните по плюсу под Compile Sources и добавьте .cpp файл (это надо будет сделать для каждого таргета):


Для обозначения того, какую форму волны будет генерировать наш осциллятор, используем enum . Сейчас по умолчанию будет генерироваться синус, но это можно изменить при помощи функции-члена класса setMode .
Объект класса хранит частоту, фазу и частоту семплирования. Значение фазы будет постоянно меняться, т. к. оно содержит в себе информацию о том, в каком моменте цикла генерации формы волны осциллятор находится. Приращение фазы осуществляется каждый семпл. Для простоты представьте себе, что полный цикл одной волны — это окружность. Значение текущего семпла — это точка на окружности. Соедините эту точку с центром окружности: угол, который образован этим отрезком и осью x и есть значение фазы. А угол, на который этот отрезок будет повернут в следующий момент — это приращение фазы.


В классе есть разные функции для установки значений параметров (для частоты и частоты семплирования, например). Но самая важная функция — это generate . Это и есть та самая функция, которая заполняет выходной буфер значениями.

Давайте добавим имплементацию этих функций в Oscillator.cpp:

Приращение фазы mPhaseIncrement зависит от mFrequency и mSampleRate , так что оно обновляется каждый раз при изменении одного из этих двух параметров. Мы могли бы вычислять его каждый семпл в ProcessDoubleReplacing , но намного лучше делать это здесь.
Функция generate на данный момент выглядит так:

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

Programmable XO Product List | Programmable Oscillator

This indicates the crystal oscillators that include a PLL circuit and can be programmed to output a desired frequency. Being able to program for any output frequency facilitates short-lead-time, small-lot production. A special programming tool is necessary to write frequency data to blank crystal oscillators. Epson's programmable crystal oscillators are the SG-8000 Series, and are programmed by the SG-Writer programming tool.

Создание аудиоплагинов, часть 16

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

Анализ спектра

Я бы хотел показать вам очень хороший бесплатный плагин: Voxengo SPAN. Его можно повесить на дорожку, и он будет показывать спектр проходящего через него сигнала. На данном этапе рановато писать собственные тестовые процедуры с FFT, так что SPAN будет незаменимым инструментом для сравнения результатов различных алгоритмов для осцилляторов. Скачивайте и устанавливайте. Запускайте SpaceBass в REAPER и сделайте следующее:

  • Ручку Mix поверните до упора налево, чтобы был слышен только первый осциллятор
  • Выберите для него меандр (квадратную форму волны)
  • Частоту среза поставьте на максимум, резонанс на минимум
  • LFO фильтра на минимум, ручка огибающей примерно посередине

С такими настройками слышна сырая форма волны первого осциллятора. Теперь повесьте SPAN на эту же дорожку после синтезатора. Зажмите высокую ноту (я использовал Ля шестой октавы) и посмотрите на спектрограмму в SPAN, должно выглядеть примерно так:


Читать ее очень просто: по оси x — частота в герцах, в этом плагине отображаются частоты от 66 Гц до 20 кГц. Шкала логарифмическая, т.е. расстояние между октавами всегда одинаковое — между До первой октавы и До второй столько же, сколько между До седьмой и восьмой. Соотношение частот соседних октав — два к одному. В то время как гармоники сигнала — это умноженная (или поделенная) на разные целые числа основная частота. А значит, гармоники распределяются по оси x неравномерно.
По оси y — амплитуда в децибелах. Таким образом очень легко определить, какая частота какую амплитуду имеет в данный момент.
В зависимости от настроек, на вашей и моей спектрограммах могут отличаться некоторые моменты, но одно ясно точно: что-то не так. Мы ожидали увидеть спектрограмму меандра — ряд пиков, амплитуда которых падает с ростом частоты, а между пиками — ничего. И уж точно мы не ожидали увидеть какие-либо спектральные составляющие ниже основной частоты (в левой части графика). Как вы помните, такая ерунда это и есть алиасинг.

Что с ним делать? Существуют разные подходы к решению. Самое аккуратное решение — синтезировать члены ряда Фурье для аппроксимации меандра. По сути, это накладывание друг на друга синусов с правильно подобранными амплитудами, начиная с фундаментальной частоты и выше, по одному синусу на гармонику. Но синтез гармоник необходимо будет прекратить при достижении частоты Найквиста. Этот подход даст идеальную полосно-ограниченную (англ. bandlimited) форму волны, все спектральные компоненты которой находятся строго в пределах между основной частотой и частотой Найквиста.
Естественно, есть одна проблема. Этот метод мог бы подойти для самых высоких октав, где фундаментальная (основная) частота настолько высока, что до частоты Найквиста остается мало гармоник. Но для нижних октав гармоник, мягко говоря, много: при частоте 44.1 кГц меандр с основной частотой 100 Гц будет иметь 219 гармоник до Найквиста, а значит в сумме надо будет вычислять sin() 220 раз каждый семпл. В полифонической модели это число еще умножается на количество играемых нот. С одной стороны, сложить синусы для каждой ноты нужно только один раз для каждой ноты. Но это так, если у нас нет никакой модуляции тона. Как только она есть, частота может меняться каждый семпл, так что нужно проделывать очень много работы.

BLIPы и BLEPы

    (Bandlimited Impulse Train, BLIT) (Minimum-Phase Bandlimited Step, MinBLEP) (Polynomial Bandlimited Step, PolyBLEP)

Последние два подхода основаны на том, что алиасинг возникает только из-за резких перепадов в форме волны. С теми формами волны, которые мы синтезируем, единственной проблемой являются эти резкие перепады. Можем ли мы сточить их, как деревяшку наждачкой? Простое скругление эквивалентно легкой низкочастотной фильтрации, а это не то, что нам надо. Нам нужно ничего не фильтровать до частоты Найквиста, и ничего не иметь выше нее, как будто это просто наложенные синусы.
Синтез меандра из синусов выглядит так:

Синим цветом обозначены накладываемые друг на друга синусы, красным — получаемый полосно-ограниченный меандр. Как видите, это не просто закругленные углы. У этой формы волны есть характерные колебания, «рябь».
Если упростить, методы BLEP сначала генерируют форму волны, как мы это делали раньше, а затем накладывает эту рябь. Это устраняет (или сильно подавляет) алиасинг.

Если вы сходили по ссылкам сверху, вы уже догадываетесь, что метод PolyBLEP — самый простой. Его мы и используем!

Класс PolyBLEPOscillator

Создайте новый класс PolyBLEPOscillator в нашем проекте. Если вы не читали предыдущих статей, скачайте готовый проект и начинайте с этого момента.

Так выглядит определение класса:

Мы наследуем публичность от Oscillator . Чтобы изменить способ синтеза, мы определяем новую член-функцию nextSample . Еще мы добавляем новую private функцию poly_blep , которая будет генерировать колебания на перепадах меандра. lastOutput хранит последнее сгенерированное значение (это понадобится только для треугольной формы волны).
Добавьте имплементацию poly_blep в PolyBLEPOscillator.cpp:

Это может выглядеть немного наворочено, но, по сути, функция почти всегда возвращает 0.0 за исключением тех случаев, когда мы близки к перепаду. Первый if для случая, когда мы в начале периода, а else if для того, когда мы почти в самом конце. Это и есть поведение пилы, потому что в ней только один перепад, между двумя периодами.

Перед тем, как имплементируем nextSample , надо кое-что поменять в классе осциллятора. Сделайте функцию nextSample в Oscillator.h виртуальной:

Это означает, что мы можем поменять поведение функции nextSample в нашем подклассе. Использование virtual в коде с критичным временем выполнения — это не самое лучшее решение. Можно использовать шаблоны (и избегать дубликации кода), но я хочу оставить объяснение на простом уровне и не отвлекаться от темы синтеза.
Измените private: на protected: . Так мы сможем получать доступ к таким параметрам как mPhase из функций-членов PolyBLEPOscillator .
Как я уже говорил, мы используем наши формы волн с алиасингом из класса Oscillator и наложим на них poly_blep . На данный момент nextSample вычисляет форму волны и осуществляет приращение фазы. Нам необходимо разделить эти несвязанные вещи.
Добавьте следующую protected функцию-член:

Эта функция будет вычислять формы волн с алиасингом. Naive здесь означает, что форма волны генерируется простым и некорректным способом. Давайте напишем ее в Oscillator.cpp (можно просто скопировать, т.к. она практически идентична Oscillator::nextSample )

  • Форма волны выбирается в зависимости от параметра mode , переданного извне (вместо mOscillatorMode )
  • Пила теперь восходящая, а не нисходящая

Т.к. эта функция содержит весь код из Oscillator::nextSample , заменим тело nextSample на это:

Здесь просто вызывается naiveWaveformForMode для вычисления формы волны и приращивается mPhase .

Генерация методом PolyBLEP

Вернемся к PolyBLEPOscillator.cpp и напишем nextSample . Начнем так:

Переменная t необходима для работы функции poly_blep . Это текущее значение фазы, поделенное на twoPI , так что оно всегда между 0 и 1 . Первый if разделяет формы волны. Для синуса антиалиасинг не нужен, так как у него есть только одна, первая гармоника — сама его основная частота. Для пилы мы сначала получаем простую форму волны от осциллятора, а затем накладываем на нее pily_blep — вот и все!
Треугольник создадим так: сначала возьмем меандр, а затем проинтегрируем его. Так как мы работаем с дискретными значениями, интеграция означает просто суммирование значений. Если прикинуть, меандр начинается со сплошных единиц, так что их суммирование даст линейное приращение. После полупериода идут сплошные минус единицы, их интегрирование даст линейный спад. Треугольник представляет из себя именно это: линейный рост и линейный спад.
Держа это в уме, напишем код сразу и для меандра, и для треугольника:

И снова мы начинаем с простой волны с алиасингом. Но в этот раз мы накладываем два PolyBLEPа. Один для начала периода, другой смещен на 0.5 периода, т.к. у меандра два перепада. У пилы только один перепад.
Не хватает только треугольника. Допишите в конец блока else :

Ранее я написал, что мы будем интегрировать меандр. Это не совсем точно. Если просто интегрировать, это приведет к огромным выходным значениям, то есть у нас получится просто чудовищный перегруз. Вместо этого нужно использовать квазиинтегратор (leaky integrator). Он суммирует новое значение со старым, но умноженным на значение немного меньше единицы. Таким образом значения не зашкаливают.
Давайте допишем приращение фазы (тут все по-старому):

Вот так просто было создать PolyBLEPOscillator !

Использование нового осциллятора

Вот и все! Запускайте наш плагин и давайте посмотрим на спектр. Как видите, эффект алиасинга очень заметно устраняется. Скриншоты до/после для сравнения:

Пила:

Меандр:

Треугольник:

А как же LFO?

Мы все еще используем старый Oscillator для LFO. Стоит ли переключиться на PolyBLEPOscillator ? На самом деле в LFO резкие границы очень желательны, при помощи них можно получать интересные эффекты. А алиасинг нас не очень волнует, т.к. основная частота обычно низкая, меньше 30 Гц. Каждая следующая гармоника имеет амплитуду ниже предыдущей, так что частоты выше Найквиста имеют очень маленькую амплитуду.

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

XO Product list | Simple Packaged Crystal Oscillator (MHz)

Epson provides a large variety of different Simple Packaged Crystal Oscillators & SAW Oscillators with various sizes, frequencies, OE/ST functions, and temperature ranges for every application. SPXO delivers excellent frequency stability without the necessity of temperature compensation and frequency control. Housing crystal units that consist of AT or BT cut fundamental or overtone and their oscillation circuit in the same package, our crystal clock oscillators are designed and manufactured to provide a stable output of signals when the set voltage is applied. Generally, when customers buy crystal units and use them in their oscillation circuits, they first need to provide analog settings prior to the installation. These include the settings of load capacitance, oscillation allowance, and variations in characteristics due to temperature changes. With Epson's crystal clock oscillators, these settings are already provided by us, so their output signals can directly control TTL and CMOS ICs. This shortens design processes for customers and contributes to standardizing parts to be used. Our crystal clock oscillators also feature a VCXO function and a Try-state function. The VCXO function enables control of output frequencies with external control voltage, whereas the output enable feature maintains the output terminals at high impedance levels by outputting signals with control signals while the power is supplied

Создание аудиоплагинов, часть 6

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

Начнем с копирования предыдущего проекта скриптом duplicate:

./duplicate.py DigitalDistortion/ Synthesis YourName

Алиасинг

Как мы уже видели в коде меандра, когда фаза возрастает до значения большего, чем два пи, в форме волны возникает мгновенный скачок от положительного максимума в текущем семпле до отрицательного в следующем. Противоположный скачок возникает, когда из mPhase вычитается twoPI и значение выражения снова становится меньше mPI . Основная идея в том, что резкие скачки сигнала означают, что в нем содержится много высокочастотных компонент.
Представьте себе, что вас попросили собрать этот скачок, используя только синусоидальные волны. Учитывая, что они гладкие, вам понадобится множество высокочастотных синусов. Вообще для создания идеального скачка теоретически понадобится бесконечное количество высокочастотных компонент, каждая со все большей частотой. Вот то же самое происходит при генерации меандра, пилы и треугольника.

Но в вычислительной технике все — конечное. Оперативная память и жесткий диск имеют конечный объем, так что для записи одной секунды звука компьютер может использовать только конечное количество значений, чтобы сохранить эту секунду. Это число (частота семплирования) может быть любым, но стандартами являются 44100, 48000 и 96000 семплов в секунду. Реже используются 176400 и 192000. Сигнал, представленный конечным количеством семплов, называется дискретным.
Чтобы описать сигнал, скачущий между -1 и 1, понадобится как минимум два семпла на цикл: один для -1, другой для 1. Значит, если семплировать с частотой 44100 раз в секунду, самой высокой корректно записанной частотой будет 22050 Гц (почитайте про частоту Найквиста)
Так что дискретным сигналом описать идеальный меандр, пилу или треугольник невозможно. Если мы все же будем пытаться это делать, то очень скоро столкнемся с эффектом алиасинга. Подробнее можно почитать здесь.

Так как же мы можем сгенерировать самую лучшую форму волны для заданной частоты дискретизации без эффектов алиасинга? «Самая лучшая» в данном случае означает «самая близкая к той, которую мы описали выше». Частота Найквиста — это некоторое ограничение в частотном диапазоне. Это ограничение означает не то, что сигнал не должен иметь пиков круче X, а то, что в сигнале не должно присутствовать частот выше X Гц. Так что нам надо переключиться в частотное представление сигнала для решения подобных задач. Мы сделаем это немного позже, а пока что — в следующем посте — нам предстоит разобраться, как читать MIDI.

XO Product list | Simple Packaged Crystal Oscillator (kHz)

Epson provides a large variety of different Simple Packaged Crystal Oscillators & SAW Oscillators with various sizes, frequencies, OE/ST functions, and temperature ranges for every application. SPXO delivers excellent frequency stability without the necessity of temperature compensation and frequency control. Housing crystal units that consist of AT or BT cut fundamental or overtone and their oscillation circuit in the same package, our crystal clock oscillators are designed and manufactured to provide a stable output of signals when the set voltage is applied. Generally, when customers buy crystal units and use them in their oscillation circuits, they first need to provide analog settings prior to the installation. These include the settings of load capacitance, oscillation allowance, and variations in characteristics due to temperature changes. With Epson's crystal clock oscillators, these settings are already provided by us, so their output signals can directly control TTL and CMOS ICs. This shortens design processes for customers and contributes to standardizing parts to be used. Our crystal clock oscillators also feature a VCXO function and a Try-state function. The VCXO function enables control of output frequencies with external control voltage, whereas the output enable feature maintains the output terminals at high impedance levels by outputting signals with control signals while the power is supplied

Crystal Oscillator

Генерация формы волны

Для синуса все просто:

Обратите внимание, что мы не используем mFrequency и mSampleRate . Мы даем приращение mPhase и ограничиваем его между 0 и twoPI . Единственной «сложной» операцией тут является вызов функции sin() , которая выполняется на уровне железа на большинстве систем.

Так выглядит пила:

  • mPhase возрастает, начиная с 0 и соскакивает обратно в ноль по достижении значения twoPI .
  • Далее, (mPhase / twoPI) возрастает с 0 до 1 и соскакивает обратно в ноль.
  • Значит, (2.0 * mPhase / twoPI) возрастает от 0 , и соскакивает обратно, как только достигает двух.
  • Когда mPhase равно 0 , выражение 1.0 - (2.0 * mPhase / twoPI) равно 1 . Пока mPhase возрастает, значение этого выражения падает и, как только достигает -1 , подскакивает до 1 .

Следующий в очереди — меандр:

Вторая половина этого кода вам уже известна. Каждый цикл длиной в twoPI . Тело условного оператора if устанавливает первую половину цикла равной 1 , вторую -1 . Когда mPhase становится больше mPI , в форме волны появляется резкий скачок. Так выглядит меандр.

Треугольник немного сложнее:

Если вы разберете по частям -1.0 + (2.0 * mPhase / twoPI) , как я это сделал раньше, то увидите, что это противоположность пиле. Абсолютное значение ( fabs ) восходящей пилы означает, что все значения ниже 0 будут инвертированы (перевернуты относительно оси x).
Значит, результирующее значение будет сначала возрастать, а потом убывать. Вычитание 0.5 выравнивает форму волны относительно нуля. Умножение на 2.0 масштабирует значение, и оно меняется от -1 до 1 . Вот и треугольник.

Давайте уже используем наш осциллятор! Включите Oscillator.h и добавьте член Oscillator в класс Synthesis :

Также нам надо переименовать mThreshold в mFrequency .
Теперь поменяйте параметры инициализации в конструкторе:

Покрутим ручку, протестируем класс. Будем менять частоту осциллятора от 50 Гц до 20 кГц (по умолчанию поставим 440).
Измените функцию createPresets :

Нам нужно передать осциллятору, какая частота семплирования сейчас используется. Это надо сделать в функции Reset :

Если мы этого не сделаем и у осциллятора будет неверно задана частота семплирования, он будет генерировать те же формы волны, но частоты будут неправильными, т. к. будет вычислено неправильное приращение фазы. Функция-член GetSampleRate наследуется из класса IPlugBase .

OnParamChange тоже надо отредактировать, чтобы можно было менять частоту ручкой:

Ну и наконец, надо использовать осциллятор в ProcessDoubleReplacing :

По сути mOscillator заполняет буфер левого канала, и мы просто копируем эти значения в буфер правого.
Давайте послушаем, как это звучит! Запускайте. Если в Xcode вылетают ошибки линкера, проверьте, добавили ли вы Oscillator.cpp в Compile Sources. Когда наша поделка запустится, будет слышен ровный тон. Покрутите ручку, и частота должна меняться.
Теперь поменяйте mOscillatorMode в Oscillator.h на этапе инициализации в конструкторе:

Перезапустите код, и теперь будет более резкий звук. Поиграйтесь с OSCILLATOR_MODE_SQUARE и OSCILLATOR_MODE_TRIANGLE , у них разные тембры.
Со всеми формами волны, кроме синуса, можно заметить, что на высоких частотах появляются странные шумы. Появляются дополнительные тоны даже ниже основной частоты. Они звучат негармонично, и когда крутишь ручку вверх и вниз, эти тоны смещаются в противоположных направлениях о_О

Читайте также: