Статьи

Начало аудио программирования в AS3

Flash Player 10 представил новые низкоуровневые API для управления аудио с AS3. В этом руководстве вы узнаете об этих API и о том, как они работают, и будете использовать их для создания простого приложения, которое может воспроизводить MP3 в обратном порядке.

Нажмите здесь, чтобы посмотреть предварительный просмотр SWF, который мы будем создавать в этом уроке. Нажмите на кнопку «Play», чтобы воспроизвести звук. Вы не можете действительно сказать, смотря или слушая это, но это не просто загруженный MP3 и затем играемый нормально; MP3 используется в качестве источника звука, и сэмплы динамически подаются на звуковой движок. Чтобы доказать это, кнопка «Обратный» воспроизводит тот же звук, только наоборот. Здесь нет ловкости рук: загружен только один MP3, и эффект разворота вычисляется на лету.


Во-первых, давайте удостоверимся, что мы в курсе метода загрузки и воспроизведения MP3 до загрузки Flash 10. То есть этот метод все еще работает во Flash 10 и выше, но мы будем изучать более продвинутый и более мощный метод в оставшейся части руководства. Если вы читаете этот учебник для ознакомления со спецификой более продвинутого метода и чувствуете, что у вас есть четкое понимание традиционного метода, не стесняйтесь переходить к шагу «Воспроизведение звука: новый путь».

Общий метод для этого процесса заключается в следующем:

  1. Создать объект Sound .
  2. Загрузите файл MP3, используя метод load объекта Sound .
  3. Как только MP3 загрузится, начните воспроизведение, вызвав метод play .
  4. Когда вы вызываете play , не забудьте сохранить объект SoundChannel который вам вернут.
  5. Когда / Если вам нужно остановить звук или отрегулировать громкость или панорамирование, вызовите соответствующие методы SoundChannel объекта SoundChannel .

Для начала давайте создадим базовый проект.

  • Создайте новый FLA-файл ActionScript 3 и сохраните его в папке проекта.
  • Создайте класс документа, чтобы идти вместе с FLA. То есть создайте новый файл ActionScript 3.0 и сохраните его как « TraditionalSound.as » в той же папке, что и ваш FLA.
  • Если вам нужен шаблон класса, введите это в ваш новый файл классов:

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    package {
        import flash.display.*;
        import flash.events.*;
        import flash.net.*;
        import flash.media.*;
        public class TraditionalSound extends Sprite {
            public function TraditionalSound() {
            }
        }
    }

Далее мы пройдемся по пяти шагам (изложенным выше), связанным с загрузкой и воспроизведением звука.


Создайте свойство экземпляра типа Sound в файле класса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
package {
  
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.media.*;
  
    public class TraditionalSound extends Sprite {
  
        private var _sound:Sound;
  
        public function TraditionalSound() {
        }
    }
}

И затем создать его экземпляр в конструкторе.

1
2
3
public function TraditionalSound() {
    _sound = new Sound();
}

Чтобы загрузить файл, вызовите метод load объекта Sound и передайте URLRequest который указывает файл MP3 для загрузки.

1
2
3
4
5
6
7
8
9
public function TraditionalSound() {
    _sound = new Sound();
    _sound.load(new URLRequest(«clip.mp3»));
    _sound.addEventListener(Event.COMPLETE, onSoundLoad);
}
  
private function onSoundLoad(e:Event):void {
    trace(«sound loaded»);
}

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


Также обратите внимание, что в идеале вы должны подключить IOErrorEvent.IO_ERROR событий IOErrorEvent.IO_ERROR , а также для ситуации, когда файл MP3 не загружается.


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

1
2
3
private function onSoundLoad(e:Event):void {
    _sound.play();
}

Протестируйте фильм сейчас, и вы должны услышать свой звук.


Теперь, когда у вас воспроизводится звук, возможно, вы хотели бы дополнительно управлять им: остановить его, отрегулировать громкость и т. Д. Если вы впервые работаете со звуком во флэш-памяти, вы можете быть удивлены, узнав, что нет stop метод на объекте Sound , и нет никакого способа управлять громкостью или панорамированием после начала воспроизведения Sound . Вместо этого есть объект SoundChannel который заботится об этих функциях. Вы создаете объект Sound.play , вызывая Sound.play , который не только запускает воспроизведение звука, но также возвращает объект SoundChannel который является воспроизводимым звуком. Итак, во-первых, нам нужно свойство для хранения объекта SoundChannel :

1
2
3
4
public class TraditionalSound extends Sprite {
  
    private var _sound:Sound;
    private var _channel:SoundChannel;

Затем нам нужно изменить наш код воспроизведения для хранения объекта SoundChannel :

1
2
3
private function onSoundLoad(e:Event):void {
    _channel = _sound.play();
}

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class TraditionalSound extends Sprite {
  
    private var _sound:Sound;
    private var _channel:SoundChannel;
  
    public function TraditionalSound() {
        _sound = new Sound();
        _sound.load(new URLRequest(«clip.mp3»));
        _sound.addEventListener(Event.COMPLETE, onSoundLoad);
        stage.addEventListener(MouseEvent.CLICK, onClick);
    }
  
    private function onSoundLoad(e:Event):void {
        _channel = _sound.play();
    }
  
    private function onClick(e:MouseEvent):void {
        _channel.stop();
    }
}

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

1
2
3
4
5
6
7
public function TraditionalSound() {
    _sound = new Sound();
    _sound.load(new URLRequest(«clip.mp3»));
    _sound.addEventListener(Event.COMPLETE, onSoundLoad);
    stage.addEventListener(MouseEvent.CLICK, onClick);
    stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
}

И сам слушатель:

1
2
3
4
5
private function onMove(e:MouseEvent):void {
    var volume:Number = 1 — (e.stageY / stage.stageHeight);
    var pan:Number = (e.stageX / (stage.stageWidth/2)) — 1;
    _channel.soundTransform = new SoundTransform(volume, pan);
}

Математика немного дикая, но она просто превращает положение мыши в числа, которые можно использовать для регулировки громкости и панорамирования. Громкость может быть от 0 до 1, а панорамирование от -1 до 1. Они SoundTransform объект SoundTransform , который затем можно установить в SoundChannel .

Теперь вы можете задаться вопросом, зачем вам нужен один объект для воспроизведения звука, а другой — для управления звуком. Сначала это немного сбивает с толку, но настройка позволяет вам иметь один объект Sound , то есть источник звука, и воспроизводить его несколько раз несколькими способами. Для простого воспроизведения песни это не слишком полезно, но представьте себе, что вы предоставляете звуки для игры. Если у вас есть звук для удара по врагу, этот звук, возможно, придется воспроизводить много раз, возможно, с перекрытием, и с отдельной громкостью и панорамированием в зависимости от того, где на экране произошло попадание. Воспроизводя один Sound на нескольких SoundChannel , вы получаете такой вид управления, но без необходимости каждый раз заново создавать источник звука.

В наших целях мы не будем вмешиваться в это, но вам нужно понять взаимосвязь между объектами Sound и SoundChannel .


Теперь, когда мы прошли ускоренный курс по загрузке и воспроизведению звука стандартным способом, мы начнем смотреть на то, как воспроизводить звук с помощью нового метода, представленного в Flash Player 10. Наш подход сначала будет включать немного Флэш-фольклор, а затем включить некоторые общие теории цифрового аудио. Затем мы создадим каркас проекта, в котором мы будем работать, а затем потратим оставшуюся часть времени на поэтапный подход к написанию кода с подробным объяснением.

Немного истории. Гении, стоящие за hobnox audiotool *, а именно Андре Мишель и Джоа Эберт, работали там, где раньше не было ни одного аудиоприложения на основе Flash, и требовали более динамичного метода создания звука на лету. Хотя им удалось добиться успеха во Flash Player 9, им надоела менее мощная звуковая реализация во Flash. Они основали сайт под названием make-some-noise.info, который был ходатайством сообщества, чтобы заставить Adobe добавить аудио функции во Flash.

Удивительно, но Adobe не только прослушала, но и выпустила в течение нескольких месяцев и выпустила некоторые ранее неожиданные функции с Flash Player 10. Хорошей новостью является то, что теперь у нас есть несколько довольно продвинутых инструментов, доступных для манипулирования звуком. Плохая новость заключается в том, что на самом деле это всего лишь небольшой набор довольно низкоуровневых API-интерфейсов, которые, хотя и являются мощными, являются лишь строительными блоками, к которым вам необходимо добавить свою изобретательность и локоть. Но это то, о чем этот урок, так что вы покрыты.

(Кампании Adobe Make Some Noise больше нет, но вы все еще можете прочитать сообщения на make-some-noise.info , а также серию постов в блоге Тиника Уро, разработчика Adobe для Flash. Проигрыватель. В этих постах он отвечает на Adobe Make Some Noise от имени Adobe и предоставляет интересную информацию о ситуации, а также некоторые из первых примеров кода. Есть три поста, которые дают хорошую дозу информации. , но не слишком длинное чтение. Часть 1 , Часть 2 и Часть 3. )

* Только личное: аудиоинструмент — безусловно, самая впечатляющая часть Flash, которую я когда-либо видел.


Чтобы полностью понять, что происходит с этой новой техникой, вы должны быть уверены, что понимаете основы цифрового аудио. Если вы чувствуете, что уже хорошо понимаете, как работает цифровой звук, в частности, характер сэмплов (это « сэмплы », как при 44 100 сэмплах в секунду на CD, а не сэмплирование басового рифа для вашей песни), тогда почувствуйте свободно переходить к следующему шагу.

Также обратите внимание, что последующее является очень кратким и кратким введением в цифровое аудио. Пожалуйста, поймите, что по этому предмету можно узнать много информации, и все, что я пытаюсь сделать, — это извлечь абсолютную информацию, связанную с нашей целью во Flash. Для получения дополнительной информации, не стесняйтесь делать больше исследований, возможно, начиная с этой статьи Audiotuts + , и, возможно, включая эту статью Википедии и эту страницу руководства Audacity , и, если этого недостаточно, завершается поиском в Интернете.

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

Нас интересует одно из этих других аудиоустройств: аналого-цифровой или аналого-цифровой преобразователь. В конечном итоге звук необходимо будет преобразовать в 1 и 0, чтобы компьютеры и другие цифровые аудиоустройства могли делать свое дело. И работа A / DC состоит в том, чтобы взять этот аналоговый электрический ток, который очень похож на изменения давления воздуха, и превратить его в поток двоичных чисел, которые действительно не похожи на оригинальный звук.

Таким образом, он работает так, что через регулярные промежутки времени A / DC берет выборку аналогового звука. Затем измеряет силу звукового сигнала в данный момент времени или амплитуду и записывает это значение в виде двоичного числа. Это делает эту выборку через регулярные интервалы, известные как частота выборки преобразования. Чем выше эта скорость, тем выше качество преобразования, но требуется больше места для хранения и вычислительной мощности. Частота дискретизации аудио CD составляет 441 кГц, или 44 100 сэмплов в секунду.


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

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


Чтобы начать с реального кодирования, мы создадим наш базовый проект. Это общий пример, но это нужно сделать. Вы можете пропустить этот шаг, просто воспользовавшись проектом Advanced-Sound-Start, включенным в пакет загрузки. Подэтапы, описанные в этом шаге, просто создадут этот файл или послужат пояснением к этим файлам.

  1. Создайте папку для нашего проекта. Моя папка называется \ advanced-sound-start \ .
  2. Создайте новый файл Flash:
    • Сделайте это ActionScript 3.0.
    • Сохраните его как AdvancedSound.fla в нашей папке проекта.
  3. Создайте пользовательский интерфейс в файле Flash:
    • Нарисуйте несколько иллюстраций, чтобы быть кнопкой «Play». Преврати его в символ и назови экземпляр » play_mc «
    • Нарисуйте несколько иллюстраций, чтобы быть кнопкой «Стоп». Преврати его в символ и назови экземпляр » stop_mc «
    • Нарисуйте несколько иллюстраций, чтобы быть кнопкой «Обратный». Преврати его в символ и назови экземпляр » reverse_mc «
  4. Создайте новый файл ActionScript, который будет служить нашим классом документа.

    • Сохраните его как AdvancedSound.as в нашей папке проекта.
    • Введите следующий типовой шаблон класса документа, если ваш редактор не предоставляет такую ​​возможность:

      01
      02
      03
      04
      05
      06
      07
      08
      09
      10
      package {
          import flash.display.*;
          import flash.events.*;
          import flash.geom.*;
          public class AdvancedSound extends Sprite {
              public function AdvancedSound() {
                  // constructor code
              }
          }
      }
  5. Мы будем помещать наш аудиокод в отдельный класс, который будет содержаться в пакете. Создайте следующие папки:
    • [корень проекта]
      • ком
        • activetuts
          • аудио
  6. Внутри звуковой папки создайте еще один файл ActionScript с именем ReversibleSound.as.

    • Введите следующий шаблон класса:

      01
      02
      03
      04
      05
      06
      07
      08
      09
      10
      11
      package com.activetuts.audio {
          import flash.events.*;
          import flash.media.*;
          import flash.net.*;
          import flash.utils.*;
          public class ReversibleSound {
              public function ReversibleSound() {
                  // constructor code
              }
          }
      }
  7. Убедитесь, что файл MP3 доступен на корневом уровне проекта. Вы можете скопировать файл clip.mp3, предоставленный в пакете загрузки (посмотрите в папку advanced-sound-start ), или использовать свой собственный MP3. Если вы используете свой собственный, вы можете переименовать его в « clip.mp3 », или же вы можете следить за появлением « clip.mp3 » на следующем шаге и изменить его соответствующим образом.

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


Теперь давайте загрузим файл MP3. Механика загрузки будет идентична той, что мы рассмотрели ранее. Однако мы завершим это в методе load класса ReversibleSound . Нам также нужно будет добавить и настроить свойство для объекта Sound . Начните с объявления свойства сразу после открытия класса и перед конструктором:

1
2
3
4
5
public class ReversibleSound {
  
    private var _soundData:Sound;
  
    public function ReversibleSound() {

Затем настройте его в конструкторе:

1
2
3
4
public function ReversibleSound() {
    _soundData = new Sound();
    _soundData.addEventListener(Event.COMPLETE, onSoundLoad);
}

Далее мы напишем метод load , который получит URL-адрес String и перенаправит его в метод load Sound . Сразу после конструктора, но до закрывающей фигурной скобки класса:

1
2
3
public function load(url:String):void {
    _soundData.load(new URLRequest(url));
}

И, наконец, давайте добавим метод onSoundLoad , обработчик события COMPLETE который мы установили в конструкторе:

1
2
3
private function onSoundLoad(e:Event):void {
    trace(«sound loaded»);
}

Чтобы проверить это, мы можем перепрыгнуть через класс AdvancedSound и убедиться, что мы создаем объект ReversibleSound и вызываем load для него. Сначала импортируйте класс:

1
2
3
4
5
6
package {
  
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import com.activetuts.audio.ReversibleSound;

Далее объявляем свойство для хранения объекта:

1
2
3
4
5
public class AdvancedSound extends Sprite {
  
    private var _sound:ReversibleSound;
  
    public function AdvancedSound() {

Затем создайте его экземпляр и вызовите load в конструкторе:

1
2
3
4
public function AdvancedSound() {
    _sound = new ReversibleSound();
    _sound.load(«clip.mp3»);
}

На данный момент, мы можем проверить это. Вы еще не услышите никакого звука, но вы должны увидеть всплывающее окно «звук загружен» на панели «Вывод», указывающее, что наши объекты подключены правильно и что наш MP3-файл загружается правильно.



Все станет немного запутанным, так что терпите меня, если я получу небольшое объяснение — счастливым.

У нас есть объект Sound , и он загружает файл MP3. Мы могли бы просто назвать play и покончить с этим. На самом деле, если вы устали от этого урока, я советую вам сделать это и прекратить читать, а затем взять стакан молока и Fruit Rollup ™, а затем вздремнуть. Никто не будет судить тебя.

Все еще здесь? Хорошо, потому что вы собираетесь заняться каким-то злым, крутым программированием Flash. Для этого нам нужно создать и сохранить другой объект Sound . Здесь вещи могут не иметь смысла сразу (и вы будете рады, что пропустили молоко). Короче говоря, нам нужен один Sound чтобы обеспечить источник звука, и другой Sound чтобы обеспечить механизм воспроизведения. Sound мы уже создали под названием « _soundData », является источником звука. Он загружает MP3 и, в будущем, предоставляет сэмплы для воспроизведения. Теперь мы создадим Sound который будет воспроизводить эти сэмплы.

В ReversibleSound создайте другое свойство прямо под первым созданным нами свойством:

1
2
3
4
5
6
public class ReversibleSound {
  
    private var _soundData:Sound;
    private var _soundPlayer:Sound;
  
    public function ReversibleSound() {

Теперь в конструкторе создайте его экземпляр:

1
2
3
4
5
6
public function ReversibleSound() {
    _soundData = new Sound();
    _soundData.addEventListener(Event.COMPLETE, onSoundLoad);
  
    _soundPlayer = new Sound();
}

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


Мы наконец-то перейдем к ключевой технике этого урока. Как упоминалось в последнем шаге, объект _soundPlayer будет обеспечивать воспроизведение, а объект _soundData предоставляет аудиоданные. Чтобы это произошло, нам нужно добавить определенное событие в объект _soundPlayer . Мы сделаем это сразу после того, как создадим это:

1
2
3
4
5
6
7
public function ReversibleSound() {
    _soundData = new Sound();
    _soundData.addEventListener(Event.COMPLETE, onSoundLoad);
  
    _soundPlayer = new Sound();
    _soundPlayer.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
}

Когда вы добавляете этот прослушиватель событий к объекту Sound , вы настраиваете его для динамической генерации звука. Обработчик события (о котором мы напишем в следующем шаге) будет вызываться во время воспроизведения Sound . Как только воспроизведение начинается, объект Sound должен воспроизводить сэмплы (помните, что это не «традиционный» звук с данными сэмплов, которые по сути являются частью объекта в форме файла MP3). Таким образом, SAMPLE_DATA событие SAMPLE_DATA , которое, по сути, является объектом Sound говорящим: «Могу ли я дать несколько сэмплов для воспроизведения?» Благодаря этому событию вы можете подавать семплы в объект Sound , а затем Sound будет воспроизводить их.

По мере воспроизведения, в конечном итоге, сэмплы заканчиваются для воспроизведения с момента первого их предоставления. «В конце концов» здесь означает «доли секунды», как правило, вы предоставляете от 2048 до 8192 выборок, то есть от 0,0464399093 до 0,1857596372 секунд при 44,1 кГц. Когда буфер сэмплов заканчивается, объект Sound снова SAMPLE_DATA событие SAMPLE_DATA , после чего вы предоставляете больше сэмплов. Sound продолжает их воспроизводить, а затем запускает событие, когда сэмплы снова заканчиваются. Этот процесс продолжается до тех пор, пока вы не прекратите предоставлять сэмплы или не stop вызов SoundChannel .

Примечание. Если вы читаете сообщения Tinic Uro, в которых говорится о том, что Adobe производит шум, вы, возможно, заметили, что во второй части он заявил, что вы можете предоставить от 512 до 8192 образцов. Когда он написал этот пост, Flash Player 10 не был выпущен, а API динамического звука не был завершен. К сожалению, это больше не так; 512 сэмплов были бы вариантом с малой задержкой для звуковых приложений, где отзывчивый звук важнее, чем сохранение циклов ЦП. Не стесняйтесь попробовать; Вы просто не получите аудио, если подключите неверный номер.


Теперь нам нужно написать этот обработчик события, чтобы мы могли продолжить обсуждение события SAMPLE_DATA .

1
2
3
4
5
private function onSampleData(e:SampleDataEvent):void {
    var bytes:ByteArray = new ByteArray();
    _soundData.extract(bytes, 2048);
    e.data.writeBytes(bytes);
}

И вот как мы подаем сэмплы в Sound . Давайте сначала проясним, что конечным результатом этого будет простое воспроизведение файла MP3, который был загружен с другим Sound . С этой целью этот метод довольно запутанный, но он помогает нам сосредоточиться на фундаментальной технике.

Давайте пройдемся по методам.

Сначала есть сигнатура метода, что неудивительно, но стоит отметить тип параметра объекта события: SampleDataEvent . Мы будем использовать этот объект события в методе, поэтому мы должны убедиться, что мы конкретны, и не использовать универсальный тип Event .

Затем мы создаем объект ByteArray . Если вы раньше не использовали ByteArray , они могут немного сбить с толку, особенно если вы ожидаете, что они будут очень похожи на обычные Array . Я не буду очень подробно ByteArray том, как работает ByteArray , достаточно лишь для того, чтобы помочь нам с нашим звуковым учебником, но вы также должны знать, что семена для будущего учебника по ByteArray s были посажены.

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

Вторая строка тела метода вызывает extract для нашего исходного объекта Sound ; то есть тот, который мы использовали для загрузки файла MP3. Здесь мы получаем сэмплы из этого Sound . Этот метод требует ByteArray (сказал ya!) В качестве первого параметра, и переданный ByteArray получит образцы данных, сохраненные в нем в результате этого процесса. Кажется странным, что вместо того, чтобы возвращать ByteArray мы передаем его и записываем в него данные, но на самом деле это распространенная техника в языках на основе Си. В этом случае более эффективно иметь метод extract который просто помещает данные в существующий ByteArray чем создает ByteArray , записывает в него и возвращает его. С этой целью мы можем сделать наш метод onSampleData более эффективным, переписав его так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ReversibleSound {
  
    private var _soundData:Sound;
    private var _soundPlayer:Sound;
    private var _bytes:ByteArray;
  
    public function ReversibleSound() {
        _soundData = new Sound();
        _soundData.addEventListener(Event.COMPLETE, onSoundLoad);
  
        _soundPlayer = new Sound();
        _soundPlayer.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
  
        _bytes = new ByteArray();
    }
  
    public function load(url:String):void {
        _soundData.load(new URLRequest(url));
    }
  
    private function onSoundLoad(e:Event):void {
        trace(«sound loaded»);
    }
  
    private function onSampleData(e:SampleDataEvent):void {
        _bytes.clear();
        _soundData.extract(_bytes, 2048);
        e.data.writeBytes(_bytes);
    }
}

Таким образом, мы создаем один объект ByteArray , сохраняем его и затем вызываем clear , вместо того чтобы создавать целый объект ByteArray каждый раз, когда нам нужно больше образцов. Как правило, создание новых объектов является дорогостоящим действием, и обычно более эффективно повторно использовать существующий объект и просто сбрасывать его атрибуты на нейтральную настройку. Это техника, известная как Object Pooling, и об этом я еще скажу в этом уроке.

Второй параметр extract — это количество сэмплов, которые можно получить из Sound . Это может быть одно из трех значений: 2048, 4096 или 8192. Не то, чтобы 2048 * 2 = 4096, а что 2048 * 4 = 8192. То, что вы выберете, зависит от ваших потребностей. Я обнаружил, что чем меньше число, тем интенсивнее должна быть Flash, поскольку она SAMPLE_DATA событие SAMPLE_DATA (потому что вы дали меньше образцов для работы в прошлый раз, и поэтому требуется больше образцов раньше). В то же время более высокие значения могут иметь странный побочный эффект, заключающийся в недостаточно быстром обновлении. Сам звук будет воспроизводиться нормально, но настройки звука, сделанные во время воспроизведения, будут отложены до следующего события SAMPLE_DATA , и, по мере увеличения времени между событиями, эта заметность становится более заметной. На данный момент это не имеет большого значения, но я решил пойти на повышение производительности до 2048 года.

Теперь вам может быть интересно, какие 2048 сэмплов взяты из исходного аудио? Как Flash узнает, с чего начать сбор данных? Ответ в третьем параметре для extract . Третий параметр указывает, с какой выборки начинать, и оттуда дальше. Однако, если он не Sound объект Sound выполняет некоторую работу, отслеживая внутренний указатель, который начинается с 0 и автоматически переходит к первому сэмплу после извлечения. Таким образом, оставляя все как есть, мы автоматически запрашиваем все сэмплы MP3 в правильном порядке.

Наконец, последняя строка onSampleData берет этот ByteArray , который теперь переполнен образцами данных, и передает его в Sound воспроизведения с writeBytes метода writeBytes свойства writeBytes , найденного в объекте SampleDataEvent переданном в обработчик событий. Нет способа записать сэмплы прямо в Sound ; Вы должны пройти через этот метод. Как только мы передадим сэмплы, мы закончим (пока в следующий раз не сработает событие), и Sound воспроизведения будет выдавать.

Мы были бы готовы протестировать это, но нам все равно нужно на самом деле сказать воспроизводимому Sound для воспроизведения, поэтому давайте подключим эту большую кнопку «play» на следующем шаге.


Этот шаг будет легкой прогулкой после последнего. Нам просто нужно немного обработать MouseEvent .

В AdvancedSound (класс документа) добавьте прослушиватель CLICK к кнопке play_mc .

1
2
3
4
5
6
public function AdvancedSound() {
    _sound = new ReversibleSound();
    _sound.load(«clip.mp3»);
  
    play_mc.addEventListener(MouseEvent.CLICK, onPlayClick);
}

И в этом прослушивателе событий вызовите пока еще неписаный метод play для ReversibleSound :

1
2
3
private function onPlayClick(e:MouseEvent):void {
    _sound.play();
}

Теперь вернитесь к ReversibleSound и напишите метод play :

1
2
3
public function play():void {
    _channel = _soundPlayer.play();
}

И, конечно, мы еще не написали это свойство _channel , поэтому объявляем это вместе с остальными свойствами:

1
2
3
4
5
6
7
8
public class ReversibleSound {
  
    private var _soundData:Sound;
    private var _soundPlayer:Sound;
    private var _bytes:ByteArray;
    private var _channel:SoundChannel;
  
    public function ReversibleSound() {

И мы должны хорошо идти. Проверьте фильм и нажмите кнопку «Воспроизвести». Если все идет хорошо, вы должны услышать воспроизведение вашего MP3 файла через Flash.


Для справки, вот полный код наших двух классов на данный момент:

AdvancedSound

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package {
  
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import com.activetuts.audio.ReversibleSound;
  
    public class AdvancedSound extends Sprite {
  
        private var _sound:ReversibleSound;
  
        public function AdvancedSound() {
            _sound = new ReversibleSound();
            _sound.load(«clip.mp3»);
  
            play_mc.addEventListener(MouseEvent.CLICK, onPlayClick);
        }
  
        private function onPlayClick(e:MouseEvent):void {
            _sound.play();
        }
    }
}

ReversibleSound

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.activetuts.audio {
  
    import flash.events.*;
    import flash.media.*;
    import flash.net.*;
    import flash.utils.*;
  
    public class ReversibleSound {
  
        private var _soundData:Sound;
        private var _soundPlayer:Sound;
        private var _bytes:ByteArray;
        private var _channel:SoundChannel;
  
        public function ReversibleSound() {
            _soundData = new Sound();
            _soundData.addEventListener(Event.COMPLETE, onSoundLoad);
  
            _soundPlayer = new Sound();
            _soundPlayer.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
  
            _bytes = new ByteArray();
        }
  
        public function load(url:String):void {
            _soundData.load(new URLRequest(url));
        }
  
        private function onSoundLoad(e:Event):void {
            //trace(«sound loaded»);
        }
  
        private function onSampleData(e:SampleDataEvent):void {
            _bytes.clear();
            _soundData.extract(_bytes, 2048);
            e.data.writeBytes(_bytes);
        }
  
        public function play():void {
            _channel = _soundPlayer.play();
        }
    }
}

Как вы можете себе представить, этот динамический аудио материал может быть довольно интенсивным для выполнения Flash. Это довольно крепко, но это не значит, что систему трудно обложить налогом. Вы должны сделать все возможное, чтобы слушатель событий SAMPLE_DATA как можно SAMPLE_DATA . Мы сделали это два шага назад, когда мы реорганизовали метод от использования нового ByteAray каждый раз для использования объекта ByteAray , только очищая его каждый раз.

Другим примером будет избегание trace s в этом методе. Вводить несколько просто для отладки — это хорошо, но слишком легко trace . Одиночные trace здесь и там хороши, но на самом деле они сами по себе являются чем-то вроде процесса налогообложения, и их привязывание к повторяющимся событиям, таким как ENTER_FRAME или быстрые ENTER_FRAME , может отрицательно ENTER_FRAME на производительности вашего фильма Flash. Аналогично, этот метод SAMPLE_DATA до 22 раз в секунду и сам по себе является чувствительной к производительности подпрограммой, поэтому, как только вы закончите со своей trace , продолжайте, закомментируйте ее или удалите ее вообще.


Последнее, что мы сделали, это подключили кнопку «play» к воспроизведению звука; давайте теперь подключим кнопку «стоп».

Поэтому мы добавим прослушиватель событий CLICK к stop_mc ролика stop_mc в AdvancedSound.as :

1
2
3
4
5
6
7
public function AdvancedSound() {
    _sound = new ReversibleSound();
    _sound.load(«clip.mp3»);
  
    play_mc.addEventListener(MouseEvent.CLICK, onPlayClick);
    stop_mc.addEventListener(MouseEvent.CLICK, onStopClick);
}

А затем напишите метод onStopClick :

1
2
3
private function onStopClick(e:MouseEvent):void {
    _sound.stop();
}

Далее нам нужно написать метод stop для класса ReversibleSound :

1
2
3
public function stop():void {
    _channel.stop();
}

И в этот момент мы должны иметь возможность запускать и останавливать звук, нажимая кнопки «воспроизведение» и «стоп» соответственно.

Если вы играете, затем останавливаетесь, а затем снова играете, вы должны заметить, что ваш звук будет звучать там, где он остановился. Это связано с методом Sound.extract и его поведением, когда третий параметр (позиция) опущен. Если вы помните, не указание параметра означает, что объект Sound будет использовать собственный внутренний указатель, который начинается с 0 и запоминает, с чего начать снова при следующем вызове. Так что мы получаем «паузу» бесплатно. Было бы достаточно просто переименовать кнопку в «паузу» и заставить кнопку «стоп» остановить звук и сбросить положение на 0, но это касательная, которой я буду сопротивляться.


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

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

Однако сказать «просто извлечь образцы в обратном порядке» и сделать это — две разные вещи. Это займет немного работы, чтобы стать функциональным, но мы сделаем это один шаг за раз.

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

Для этого мы добавим свойство и связанный установщик / получатель, чтобы указать, что обратное воспроизведение должно быть / действительно. В ReversibleSound.as добавьте частное свойство в той же области, что и остальные свойства:

1
2
3
4
5
6
7
8
9
public class ReversibleSound {
  
    private var _soundData:Sound;
    private var _soundPlayer:Sound;
    private var _bytes:ByteArray;
    private var _channel:SoundChannel;
    private var _isReversing:Boolean;
  
    public function ReversibleSound() {

Затем добавьте пару методов получения / установки для него:

1
2
3
4
5
6
public function get isReversing():Boolean {
    return _isReversing;
}
public function set isReversing(reverse:Boolean):void {
    _isReversing = reverse;
}

Теперь вернитесь к AdvancedSound.as и подключите событие CLICK для кнопки reverse_mc :

1
2
3
4
5
6
7
8
public function AdvancedSound() {
    _sound = new ReversibleSound();
    _sound.load(«clip.mp3»);
  
    play_mc.addEventListener(MouseEvent.CLICK, onPlayClick);
    stop_mc.addEventListener(MouseEvent.CLICK, onStopClick);
    reverse_mc.addEventListener(MouseEvent.CLICK, onReverseClick);
}

А затем напишите обработчик:

1
2
3
4
5
6
7
8
private function onReverseClick(e:MouseEvent):void {
    _sound.isReversing = !_sound.isReversing;
    if (_sound.isReversing) {
        reverse_mc.transform.colorTransform = new ColorTransform(1, 1, 1, 1, -20, -20, -20, 0);
    } else {
        reverse_mc.transform.colorTransform = new ColorTransform();
    }
}

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

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


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

Давайте начнем с объявления свойства в ReversibleSound.as для отслеживания нашей позиции.

1
private var _position:int;

Затем перейдите к вашему set isReversingсеттеру, и мы установим эту позицию там:

1
2
3
4
5
6
public function set isReversing(reverse:Boolean):void {
    _isReversing = reverse;
    if (_isReversing) {
        _position = (_soundData.length / 1000) * 44100;
    }
}

Если мы включаем обратное воспроизведение, то мы установим _position. То, как мы это установили, требует некоторого объяснения.

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

Для этого нам нужно открыть банку с математикой. Было бы замечательно, если бы мы могли легко получить количество сэмплов в данном задании Sound, но вместо этого самое близкое, что мы можем получить, — это lengthсвойство, которое дает нам сколько миллисекунд длится звук. Если мы разделим это число на 1000, мы получим, сколько секунд длится звук. И так как мы собираемся предположить, что MP3 кодируется с частотой дискретизации 44,1 кГц, мы можем умножить количество секунд на 44100, чтобы получить количество выборок.

Это, конечно, можно упростить до:

1
_position = _soundData.length * 44.1;

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

Предполагая, что 44,1 кГц является справедливым допущением; Flash может воспроизводить файлы MP3 (используя традиционный метод), только если частота дискретизации составляет 44,1 кГц, 22,05 кГц или 11,025 кГц. А при использовании динамического генерирования звука при Soundзапросе данных выборки ожидается, что выборки будут предоставлены с частотой 44,1 кГц. Интуиция моего программиста хочет сделать это 44100и превратить его в свойство, которое можно настроить, и в этом нет ничего плохого, но также может быть опасно неправильно настроить свойство. Имейте это в виду, однако, если вы загружаете звук, который не 44,1 кГц. Если вы получаете ошибки (потому что вы переоценили количество сэмплов) или если ваше воспроизведение происходит на половинной скорости (потому что вы недооценили), попробуйте перекодировать MP3 в 44,1 кГц.

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


Теперь, если мы находимся в обратном порядке, нам нужно соответствующим образом обновить это _positionсвойство при запросе образцов данных. Сначала мы добавим размер буфера выборки в свойство, чтобы его было проще использовать последовательно в нескольких местах, где оно нам понадобится. Еще в ReversibleSound.as , создайте приватное свойство для хранения размера нашего буфера. Также создайте три открытые статические константы для хранения допустимых значений для буфера:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
public class ReversibleSound {
  
    private var _soundData:Sound;
    private var _soundPlayer:Sound;
    private var _bytes:ByteArray;
    private var _channel:SoundChannel;
    private var _isReversing:Boolean;
    private var _position:int;
    private var _bufferSize:int;
  
    public static const BUFFER_SMALL:int = 2048;
    public static const BUFFER_MEDIUM:int = 4096;
    public static const BUFFER_LARGE:int = 8192;
  
    public function ReversibleSound() {

А затем мы создадим необязательный параметр для конструктора, который по умолчанию будет «маленьким», и установим _bufferSizeсвойство.

01
02
03
04
05
06
07
08
09
10
11
public function ReversibleSound(bufferSize:int = BUFFER_SMALL) {
    _soundData = new Sound();
    _soundData.addEventListener(Event.COMPLETE, onSoundLoad);
  
    _soundPlayer = new Sound();
    _soundPlayer.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
  
    _bytes = new ByteArray();
  
    _bufferSize = bufferSize;
}

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

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

1
2
3
4
5
6
7
8
9
private function onSampleData(e:SampleDataEvent):void {
    _bytes.clear();
    if (_isReversing) {
        _position -= _bufferSize;
    } else {
        _soundData.extract(_bytes, _bufferSize);
    }
    e.data.writeBytes(_bytes);
}

Что мы делаем, так это проверяем, разворачиваемся ли мы или нет. Если нет (это был бы elseблок), мы делаем именно то, что делали раньше: просто извлекаем образцы и передаем их в Sound. Однако, если мы являемся задним ходом, нам необходимо обновить нашу внутреннюю позицию по вычесть из него размера буфера. Таким образом, мы будем уменьшать позицию на размер буфера, чтобы мы могли извлекать сэмплы порциями, только назад.

Обратите внимание, что мы также обновили extractметод воспроизведения вперед, чтобы использовать его _bufferSizeвместо жесткого кода 2048.


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

К счастью, у нас уже есть это значение: это то, над чем _positionмы работаем. Чтобы извлечь наши образцы, нам просто нужно добавить следующую выделенную строку:

01
02
03
04
05
06
07
08
09
10
private function onSampleData(e:SampleDataEvent):void {
    _bytes.clear();
    if (_isReversing) {
        _position -= _bufferSize;
        _soundData.extract(_bytes, _bufferSize, _position);
    } else {
        _soundData.extract(_bytes, _bufferSize);
    }
    e.data.writeBytes(_bytes);
}

И не стесняйтесь проверить его (нажмите «Обратный», затем «Играть»); вы получите обратное аудио. Вид. Это определенно звучит наоборот. Но это также звучит немного нервно. Мы рассмотрим это на следующем шаге.


Причина, по которой звук звучит немного прерывисто, заключается в том, что пока мы извлекаем сэмплы в обратном порядке, мы извлекаем только фрагменты сэмплов, 2048 за раз, в обратном порядке. Сами куски ByteArrayнаходятся в прямом порядке. Таким образом, мы перевернули фрагменты, но отдельные образцы внутри этих фрагментов все еще находятся в прямом порядке. Это все равно, что взять колоду карт, взять пять сверху и положить их в сторону, затем взять следующие пять сверху и положить их сверху новой стопки, и так далее. Мы перепутали порядок карт, и вид обратные их так , карты , которые были в верхней части палубы теперь на дне, и наоборот. Но они не полностью изменены.

Чтобы получить истинное обратное воспроизведение, нам нужно затем обратить вспять содержимое the ByteArrayпосле того, как мы извлечем сэмплы. Мы создадим новый метод для этой цели. Поместите это где-нибудь в ReversibleSound.as :

01
02
03
04
05
06
07
08
09
10
private function reverseBytes(inBytes:ByteArray):ByteArray {
    _reversedBytes.clear();
  
    for (var i:int = inBytes.length - 4; i >= 0; i -= 4) {
        inBytes.position = i;
        _reversedBytes.writeFloat(inBytes.readFloat()); //4 bytes each time.
    }
  
    return _reversedBytes;
}

Этот метод нуждается в другом ByteArrayобъекте для выполнения реверсирования, и вместо того, чтобы создавать новый ByteArrayкаждый раз, когда вызывается этот метод, мы можем сэкономить некоторые накладные расходы, создав один в свойстве и затем clearвызывая его каждый раз. Это _reversedBytesиспользуется в этом методе. Нам нужно объявить и создать экземпляр этого свойства:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ReversibleSound {
  
    private var _soundData:Sound;
    private var _soundPlayer:Sound;
    private var _bytes:ByteArray;
    private var _channel:SoundChannel;
    private var _isReversing:Boolean;
    private var _position:int;
    private var _bufferSize:int;
    private var _reversedBytes:ByteArray;
  
    public static const BUFFER_SMALL:int = 2048;
    public static const BUFFER_MEDIUM:int = 4096;
    public static const BUFFER_LARGE:int = 8192;
  
    public function ReversibleSound(bufferSize:int = BUFFER_SMALL) {
        _soundData = new Sound();
        _soundData.addEventListener(Event.COMPLETE, onSoundLoad);
  
        _soundPlayer = new Sound();
        _soundPlayer.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
  
        _bytes = new ByteArray();
        _reversedBytes = new ByteArray();
  
        _bufferSize = bufferSize;
    }

Возвращаясь к reverseBytesметоду, его базовая логика просто перебирает входные данные ByteArrayв обратном порядке и считывает из него числа для записи в обратном порядке ByteArray. Опять же, я не буду останавливаться на механике ByteArrayздесь, но я укажу, что сэмплы, хранящиеся в ByteArray, как из extractметода, так и из того, что Soundожидает воспроизведение , записываются как числа с плавающей запятой: 32-битные числа с плавающей запятой , Таким образом, мы можем читать отдельные образцы readFloatи сохранять их writeFloat. Поскольку positiona ByteArrayосновано на байтах, и в 32-битном числе 4 байта (8 бит = 1 байт), мы можем пройти ввод ByteArrayс шагами по 4 и получить правильную начальную точку для следующего (или предыдущего) в зависимости от того, как вы на это смотрите) образец.

Когда все сказано и сделано, метод возвращает идеальное изменение ввода ByteArray.


Давайте теперь использовать этот метод в onSampleData:

01
02
03
04
05
06
07
08
09
10
11
private function onSampleData(e:SampleDataEvent):void {
    _bytes.clear();
    if (_isReversing) {
        _position -= _bufferSize;
        _soundData.extract(_bytes, _bufferSize, _position);
        _bytes = reverseBytes(_bytes);
    } else {
        _soundData.extract(_bytes, _bufferSize);
    }
    e.data.writeBytes(_bytes);
}

Нам просто нужно передать _bytesпосле извлечения, а затем мы можем сохранить результат обратно, _bytesчтобы его можно было использовать для записи сэмплов в воспроизведение Sound.

Идите и попробуйте. Нажмите кнопку «Обратный», затем «Воспроизвести», и … о, черт, вы, возможно, слышали самое начало какого-то обратного звука, но потом ничего не получилось (Или, в зависимости от вашего звука, вы можете не слышать что угодно, если в концовке есть часть тишины как часть MP3). Что за двойка?

На данный момент, мы должны быть осторожны с тем, как мы управляем ByteArrays. Наша попытка сэкономить некоторые накладные расходы, ByteArrayзаранее создавая s и повторно используя их для каждого метода, на самом деле возвращает нас к укусам. Но есть способ обойти проблему, и он предполагает, да, создание другого ByteArray.

На этот раз нам просто нужно объявить свойство (или даже переменную метода, хотя мы можем — снова — сохранить некоторую обработку, объявив ее один раз как свойство, а не несколько раз как переменную метода). Давайте сделаем это там, где объявлены остальные свойства:

1
private var _samplesToUse:ByteArray;

И давайте использовать его в onSampleDataметоде:

01
02
03
04
05
06
07
08
09
10
11
12
private function onSampleData(e:SampleDataEvent):void {
    _bytes.clear();
    if (_isReversing) {
        _position -= _bufferSize;
        _soundData.extract(_bytes, _bufferSize, _position);
        _samplesToUse = reverseBytes(_bytes);
    } else {
        _soundData.extract(_bytes, _bufferSize);
        _samplesToUse = _bytes;
    }
    e.data.writeBytes(_samplesToUse);
}

Мы добавляем две строки логики, по одной для каждого случая if/else. Мы просто назначаем _samplesToUseлибо _bytes(для обычного воспроизведения), либо возврату reverseBytes(для обратного воспроизведения). Тогда мы используем _samplesToUse, а не _bytes, при отправке образцов на Soundобъект с writeBytes.

Это может иметь смысл, и может быть неясно, зачем мы вообще это делаем.

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

  • Когда мы переходим _bytesв reverseBytes, мы используем его в качестве источника данных для обращения. Поскольку мы должны петли над этим источником данных, и записать его в обратном в другой ByteArray, мы имеем второе ByteArrayсвойство, _reversedBytes.
  • Оба _bytesи _reversedBytesдержатся в целях эффективности. reverseBytesзатем возвращается _reversedBytes, а затем мы установили , что _bytesв строке 49, в onSampleData. После одного вызова мы фактически указали _bytesи _reversedBytesна один и тот же ByteArrayобъект.
  • Во второй раз мы фактически передаем этот ByteArrayобъект с двойной ссылкой reverseBytes, и теперь этот же объект является исходными данными и ByteArrayпредназначен для хранения обратных выборок.
  • В этот момент у Флэша возникают проблемы с предотвращением взрыва вселенной, и он просто перестает думать об этом («Новая миссия: откажись от этой миссии!»). Поэтому мы обойдем проблему, введя третье ByteArrayсвойство, но понимаем, что это не третий ByteArrayобъект, а просто свойство, используемое для ссылки. Нам не важно , который ByteArrayполучает хранится в нем, так что мы используем , что один в качестве указателя в зависимости от того , кого мы хотим использовать и передать , что в writeBytes.

Теперь вы должны теперь услышать совершенно перевернутое аудио!


Мы должны рассмотреть, что произойдет, когда мы подойдем к началу звука. Как _postiionуменьшается, в какой-то момент он станет отрицательным. Это недопустимая позиция, но она ниже 0 и приведет extractк тому, что она начнется с начала и будет двигаться вперед автоматически, как мы делаем, если не в обратном направлении.

Поэтому каждый раз, когда мы уменьшаемся _position, нам нужно проверять, чтобы убедиться, что он равен 0 или выше. Если нет, то мы должны считать, что звук прошел, и мы можем остановиться. Обновите onSampleDataметод до этого:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private function onSampleData(e:SampleDataEvent):void {
    _bytes.clear();
    if (_isReversing) {
        _position -= _bufferSize;
        if (_position < 0) {
            stop();
            isReversing = true;
            return;
        }
        _soundData.extract(_bytes, _bufferSize, _position);
    } else {
        _soundData.extract(_bytes, _bufferSize);
    }
    e.data.writeBytes(_bytes);
}

Если _positionсвойство становится отрицательным, то мы можем позвонить, stop()чтобы убедиться, что звук прекратился. Затем мы можем сбросить _positionсвойство до конца звука снова, так что оно готово начать снова, если play()произойдет вызов следующего. Мы делаем это, просто устанавливая isReversingсвойство trueснова. Это уже true, но когда оно установлено, trueоно также устанавливает _positionсвойство в конце. Наконец, мы returnрано, чтобы никакие образцы не были извлечены или отправлены на звук.


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

Надеюсь, тебе понравилось с этим. Я вернусь с другим учебником по Flash аудио, так что если вам понравилась эта статья, следите за обновлениями.