Помогни ни да направим Uroci.net по - богат! Добави урок

Real-Time еквалайзер с ActionScript 3.0

accorp   трудност:    видян: 12084

Може би някои от вас са виждали програми, които "анализират" даден mp3 файл и съставят масив наподобяващ топографска карта и подобно на такава показва стойности за звуковите вълни. Чрез тази карта може да се направи еквалайзер (като този в Winamp), който всъщност е "действителен", но така или иначе предварителната подготовка на файла е задължителна, което е огромно неудобство.

В ActionScript 3.0 има разработен клас, който изготвя подобна на гореописаната карта, с тази разлика, че всичко става real-time, т.е. докато звука се възпроизвежда, а и еквалайзера е напълно реален.

Какво трябва да знаете за SoundMixer класа

Той е статичен клас, т.е. не се нуждае от инициализиране като отделен обект, няма нужда да се "пуска" и "спира" (докато звука се спира, оставя на пауза и прочее), защото връща нули когато звука не се възпроизвежда. Другата важна особеност е, че анализа на звуците е глобален, което ще рече, че всички текущи звуци във Flash Player ще бъдат "прихванати" поради което не се посочва точно определенa Sound инстанция. Това е както предимство, така и може да се окаже недостатък.

Нека първо възпроизведем звука

1. На първо място ще поставим инстанцията на клас Sound:

var snd:Sound = new Sound();

От тук нататък това ще е "главната" променлива, към която ще се обръщаме.

2. Метода load() на клас Sound изисква подаването на параметър от клас URLRequest, затова ще го направим още сега:

var rq:URLRequest = new URLRequest("http://freedownloads.last.fm/download/104066112/Silica.mp3");


3. До тук, няма почти нищо ново, нищо което да е особено различно от методаиката в AS2.0. В AS3.0 обаче се нуждаем от "канал" в който да пуснем звука. Ако погледнете в Help на Flash CS3 за Sound.play() ще видите, че метода всъщност връща друг тип обект: SoundChannel. Чрез този канал се управляват някои свойства на звука, но за сега само ще декларираме една променлива – инстанция на SoundChannel:

var channel:SoundChannel;


4. До тук имаме три променливи. Време е да започне зареждането на самия mp3 файл:

snd.load(rq);

Файла, който избрах, е напълно случаен (безплатен в www.last.fm). След този ред стрийма ще започне моментално, но възпроизвеждането не – това се добавя "ръчно", т.е. с метода за който говорихме по-горе.

5. channel = snd.play();

6. Преди да започнем самото прихващане ще трябва да декларираме още една променлива, ключова за метода на SoundMixer и това е така наречения ByteArray (нов вид масив в ActionScript 3.0). Това е кодиран масив, който, според описанието в Help, позволява оптимизация при работа с двоични данни. Т.е. това е хубав масив. Има и още няколко особености, но тях ще разгледаме по-късно.

var ba:ByteArray = new ByteArray();


7. Следващото което трябва да направим е да "отворим" един процес, с който постоянно ще следим звуците и ще извикваме определени инструкции:

stage.addEventListener(Event.ENTER_FRAME, tracker);
function tracker(e:Event){
//тук ще продължим с инструкциите
}


Може би ще работи по прецизно, ако вдигнете fps на 30. Така 30 пъти в секунда ще анализираме звука.

8. Във функцията tracker ще извикаме SoundMixer.computeSpectrum() метода, в който е и цялата магия:

SoundMixer.computeSpectrum(ba, true, 0);

Първия параметър на функцията е битовия масив, където ще се запишат стойностите. Стойносите са в интервал от -1.0 до 1.0. За едно извикване на метода получаваме 512 стойности – първите 256 са за левия канал, а вторите 256 – за десния. Ние ще работим само с първите 256, така или иначе няма да пипаме "ляво" и "дясно" – предполага се, че са еднакви.

Втория параметър е булева променлива. Когато е зададен false (което е и по подразбиране) спектъра е "груба" звукова вълна, а при true – честотен спектър. Този честотен спектър е стандартен за повечето плеъри, като Winamp например, като при него ниските честоти са отляво, т.е. в първите стойности на масива (първите стойности от първите 256), а високите честоти – отдясно.

Третия параметър е sampleRate на mp3 файла. По подразбиране този параметър е 0, което означава 44.1 kHz. 1 означава 22.045 kHz, 2 – 11.025 kHz и т.н.

Тук трябва да вметна, че при всяко прихващане стойностите се записват върху старите, т.е. масива няма да се "препълни", а и не се налага да го нулираме ръчно, което е хубаво.

9. Сканирахме песента и записахме стойностите в масива. Сега трябва да обходим масива и за определени стойности ще изпълним една функция (която ще напишем по-късно). Тук трябва да спомена, че ByteArray няма индекси, като при обикновените масиви, а прочитането му няколкопоредни пъти измества "позицията" напред. Т.е. трябва да прочетем масива 256 пъти за да извадим всичките 256 стойности. Трябва да кажа и че този масив си има определени методи за четене според стойностите записани в него. Е, computeSpectrum връща Float стойности, означава, че ще използваме метода readFloat(). Нищо сложно:

for(var i:Number=0; i<256; i+=4){
drawSpec(ba.readFloat(), i);
}


256 такта на цикъла извършван 30 пъти в секунда е малко много, нали? Още повече, ако трябваше да го правим за два канала. Затова ще направим стъпката през 4. Аз няма да имам други неща на сцената затова ще я направя 4, но препоръчвам да се направи през 16 или 32 за да се ограничи "товара". В цикъла извикваме функцията drawSpec, която ще напишем след малко, като подаваме двама параметъра – стойността в ByteArray и стъпката на инкрементация.

10. Рисуваме:

function drawSpec(rf:Number, itr:Number){
var num:Number = rf * 100;
graphics.lineStyle(1, 0x000000);
graphics.beginFill(0xffffff);
graphics.drawRect((itr/4)*8 + 10, 200, 8, -num);
}
Тук graphics е клас към stage, т.е. ще рисуваме директно на сцената. С първата променлива прихващаме стойноста подадена от ByteArray-а. Ако си спомняте стойностите са в интервал от -1.0 до 1.0... само че това не е точно така. Ако изведете подадените стойности ще видите, че са от 0.0 до 1.0. Тази стойност ще използваме за височина на стълбчета, само че 1 е малко, затова ще разширим интервала от 0.0 до 100.0.

Следващите два реда декларират стила на линиите и цвета на стълбчето – 1 px черно и бял цвят за стълбчето.

Накрая извикваме и метода drawRect, с който ще нарисуваме правоъгълници, т.е. самите стълбчета. Параметрите, който метода очаква са x, y, width, height. На първо място ще подредим всички стълбчета едно до друго, като започваме от 10-тия пиксел и увеличаваме по 8 – толкова ще сложим за ширина на стълбчето. Втория параметър е y, искам всичките да са на еднаква височина, като при мен 200 е средата на сцената. Вече казахме, че ширината на стълбчето е 8 пиксела, а височината е стойността num. Тъй като, по подразбиране, registration point на стълбчето е горе вляво ще сложим минус на височината за да "подскачат", а не да се "спускат.

11. Сега се върнете във функцията tracker и добавете един ред преди for цикъла:

graphics.clear();

Не искаме стълбчетата да се натрупват едно върху друго.

Това е кода, който трябва да сте получили:

var snd:Sound = new Sound();
var rq:URLRequest = new URLRequest("http://freedownloads.last.fm/download/104066112/Silica.mp3");
var channel:SoundChannel;
snd.load(rq);
channel = snd.play();
var ba:ByteArray = new ByteArray();
stage.addEventListener(Event.ENTER_FRAME, tracker);
function tracker(e:Event){
SoundMixer.computeSpectrum(ba, true, 0);
graphics.clear();
for(var i:Number=0; i<256; i+=4){
drawSpec(ba.readFloat(), i);
}
}
function drawSpec(rf:Number, itr:Number){
var num:Number = rf * 100;
graphics.lineStyle(1, 0x000000);
graphics.beginFill(0xffffff);
graphics.drawRect((itr/4)*8 + 10, 200, 8, -num);
}

За напреднали: Mолучихме необходимото, локално всичко си работи. Обаче качено на сървър - не. Това се получава заради рестрикциите наложени на метода computeSpectrum. Ако файлът, който зареждате, е от същия домейн като SWF файла, вероятно няма да имате проблем, но при горепосочения файл не става. Тук има малка хитрост, която уж използва така наречена "полица", като търси определен XML файл, но когато го няма просто разрешава опрациите от домейн към домейн.

Трябва да извикаме и още един клас: SoundLoaderContext.

Преди основната звукова променлива декларирайте:

var stcon:SoundLoaderContext = new SoundLoaderContext(1000, true);

Първия параметър в конструктора е време за буфериране (в милисекунди), а другия – търсенето на полицата, за която говорихме.

След това при метода load() към Sound обекта, за параметър е посочен URLRequest-a, но има и един незадължителен параметър – там подаваме инстанцията на SoundLoaderContext:

snd.load(rq, stcon);

Това е примерна употреба на SoundMixer. Надявам се, че съм бил полезен.

Автор: accorp

 



Коментари (3)

d0brin на 21.08 2008 в 12:16ч.
тва нешо е супер сложноо нищо не рабзрха как да го нрапва... :D
vasill4o на 26.08 2008 в 17:03ч.
Супер е урока. Мерси.
da4ka13 на 02.09 2008 в 10:58ч.
Урока много ме израдва само като прочетох името, а като го направих още повече... а като го и разбрах съвсем...
Само че аз използвам един Timer за TimerEvent.TIMER на всеки 30 милисекунди и само си нагласям самото филмче на около 30 fps за да има полза
Реших че така ще може по-лесно да се редактира периода на прерисуването и по-удобно да се използва в друг swf който не е със същия, но близък fps

Регистрирайте се, за да добавите коментар


Калдейта ЕООД - © 2003-2010. Всички права запазени.
Препоръчваме: Национален Бизнес | Bomba.bg | IT Новини | Диплома.бг | TRAVEL туризъм | Реферати | AmAm.bg | Иде.ли | Курсови работи | Фото Форум | Spodeli.net | Фото-Култ | Atol.bg | Elmaz.com | MobileBulgaria.com | Казанлък.Com