Tải bản đầy đủ - 0 (trang)
Hack 77. Show Audio Information While Playing SoundHack

Hack 77. Show Audio Information While Playing SoundHack

Tải bản đầy đủ - 0trang

Drawingthislevelmeterisprettystraightforward:createa

JPanelwhosepaint()methodclearstheGraphics,getstheline

level,andfillsarectanglestartingat(0,0)withaheightequal

tothecomponent'sheightandawidthequaltotheleveltimes

thecomponent'swidth.Thenyouneedtosetupananimation

loopajavax.swing.Timerisconvenientbecauseitavoidsany

thread-safetyissueswhiledoingthepaintingtorepeatedlycall

repaint()onthemeter.

CombinethistogetherandyouhavetheDataLineInfoGUI,seenin

Example10-10.Notethattoplaytheaudio,itusesthe

PCMFilePlayerclassfromtheprevioushack,soyoucanusean

arbitrarilylongAIFForWAV,aslongasitscontentsare

uncompressedPCMdata.



Example10-10.Displayingaudioformat

information

importjavax.sound.sampled.*;

publicclassDataLineInfoGUIextendsJPanel{







PCMFilePlayerplayer;

JButtonstartButton;





publicDataLineInfoGUI(Filef){





super();





try{







player=newPCMFilePlayer(f);





}catch(Exceptionioe){







add(newJLabel("Error:"+









ioe.getMessage()));

return;





}





DataLineline=player.getLine();





//layout







//line1:name





setLayout(newBoxLayout(this,BoxLayout.Y_AXI





add(newJLabel("File:"+

player.getFile().getName()));





//line2:levels





add(newDataLineLevelMeter(line));





//line3:formatinfoastextarea





AudioFormatformat=line.getFormat();





JTextAreata=newJTextArea();





ta.setBorder(newTitledBorder("Format"));





ta.append("Encoding:"+

format.getEncoding().toString()+"\n");

ta.append("Bits/sample:"+









format.getSampleSizeInBits()





ta.append("Endianness:"+

(format.isBigEndian()?"big":"little")+

format.getFrameSize()+"\n");





ta.append("Framerate:"+

format.getFrameRate()+"\n");

add(ta);























//nowstartplaying



player.start();

}

publicstaticvoidmain(String[]args){



JFileChooserchooser=newJFileChooser();



chooser.showOpenDialog(null);



Filefile=chooser.getSelectedFile();



DataLineInfoGUIdemo=





newDataLineInfoGUI(file);



















JFramef=newJFrame("JavaSoundinfo");



f.getContentPane().add(demo);



f.pack();



f.setVisible(true);

}

classDataLineLevelMeterextendsJPanel{







DataLineline;





floatlevel=0.0f;





publicDataLineLevelMeter(DataLinel){







line=l;







Timertimer=









newTimer(50,









newActionListener(){









publicvoidactionPerformed













level=line.ge













repaint();











}









});

timer.start();





}





publicvoidpaint(Graphicsg){







Dimensiond=getSize();







g.setColor(Color.green);







intmeterWidth=(int)(level*(float)







g.fillRect(0,0,meterWidth,d.height)





}





}



}



Thisisaprettystraightforwardimplementationofthestrategy

sketchedoutpreviously:theclassisaJPanelwithaBoxLayoutto

whichyoucanaddanarbitrarynumberofrows.Thefirstisthe

nameofthefile,thesecondisthelevelmeter,andthethirdisa

JTextAreatowhichyoucanappendvariousfieldspulledfromthe

AudioFormat.

Thelevelmeter'sconstructortakescareofsettingupitsown

repaintcallbacks,sothere'snobabysittingrequiredonthepart

ofthecaller.Allthat'sleftfortheconstructoristostartthe

playertobeginfeedingbytestotheLine.



10.9.2.TestingItOut

LaunchtheDataLineGUIapplicationandyou'llgetafile-selection

dialog.ChooseasuitableAIFForWAV,andyou'llseetheGUI

showninFigure10-8.



Figure10-8.Audioplayerdisplaywithformat

information



ThisisallwellandgoodforasimpleGUI,butthere'sone

problem:wheretheheckisourlevelmeter?!Itshouldbe

betweenthefilenameandthetextarea,butit'stotallynot

there!

Initially,Isuspectedmyrepaintcodewashosed,butitall

seemedcorrect.So,rightafterfiguringoutthemeterwidth,I

addedasanity-checkdebugline:

System.out.println("level="+level);



AndwhenIranit,IgotaresultthatIreallydidn'twanttosee:







[aeris:HacksBook/Media/x11]cadamson%javaDataLineInfo

gotPCMformat





















gotinfo

gotline

openedline

level=0.0

level=0.0

level=0.0

level=0.0

level=0.0



Andthatwasonareallyloudsong,soitwasn'tjustaslowfade

in.Ilookedaroundtoseeiftherewassomethingspecialyou

havetodoforgetLevel()towork,buttherewasn't.

ThenIGoogled,andfoundthisposttothejavasound-interest

mailinglistfromFebruary2003:

Table10-1.

Date:



Mon,17Feb200322:31:21-0800



Reply-To:



DiscussionlistforJavaSoundAPI











Sender:



DiscussionlistforJavaSoundAPI











From:



FlorianBomers



Organization:



SunMicrosystemsInc.



Subject:



Re:DataLine.getLevel()?



Comments:To:



knute@frazmtn.com



Content-Type:



text/plain;charset=us-ascii



Unfortunately,itisnotimplemented.(actually,inmyprivateopinion,

itisaquestionablemethodanyway:usuallysoundcarddriversdonot

providesuchaprimitive,sotheJavaSoundimplementationhasto

calculatethis"level"onitsown.Buttherearemanydifferent

algorithmstodoso,suiteddependingforwhatthe"level"isneeded

for,anditwouldpossiblyeatunnecessarilyprocessorresources.SoI

guessit'sbestifeverybodydoesthecalculationofthe"level"onhis

ownonthebuffersreceivedbytheTDLorwrittentotheSDL,

respectively.Easyandfastalgorithmsaremaximum,movingaverage,block

average,power).



sorry…

Florian



KnuteJohnsonwrote:

>

>AnybodyknowifDataLine.getLevel()isimplemented?AllIge

>onSourceDataLinesand-1.0onTargetDataLines.

>

>Thanks,

>

>KnuteJohnson



Infact,alittlefurtherresearchshowsthatthefactthat

DataLine.getLevel()alwaysreturnsUNKNOWN_LEVELwasfiledasbug

4297101intheJavaBugParadeonDecember6,1999.Five

yearslater,it'sstillnotfixed,thoughitlooksliketherewasat

leastanattempttofixitforTiger(J2SE5.0)afixthatwas

abandonedinAugust2003.



Bytheway,wouldn'tithavesavedalotofpeoplealotoftimeifthey

disclosedintheJavadocthatthismethodisanoop?ButIdigress



So,thelevelmeterisnotgoingtoworknotbecauseofthe

graphics,butbecausethere'snowaytogetanaccuratelevel.

Oristhere?



10.9.3.HackingtheHack

Florian'smessagetojavasound-interestsaysitisbestif

"everybodydoesthecalculationofthe'level'onhisown[,

based]onthebuffersreceivedbytheTDL[(TargetDataLine,

usuallyusedbycapturedevices)]orwrittentotheSDL

[(SourceDataLine)],respectively."

Settingasidetheargumentofduplicationofeffort,notethat

thebuffershespeaksofareavailableinthehackcode;it's

whatthePCMFilePlayerreadsfromthefileandwritestotheLine

(specifically,aSourceDataLine,asFlorian'smessagenotes).So,in

theoryatleast,thiscanbedone.Butit'snotgoingtobepretty.

First,createanewDataLineInfoGUI2classthatisidenticaltothe

onefromearlierinthishack,exceptthatinsteadofusinga

PCMFilePlayer,itusesaPCMFilePlayerLeveler,aclassthatwillbe

definednext.

ThisnewclassisprettymuchthesameastheoldPCMFilePlayer,

exceptthatoneachtimethroughthewhileloop,asitreadsthe

bufferandwritesittotheline,itwillcallamethodtoscan

throughthebufferanddeterminealevelforthisgroupof

samples.So,afterreadingthebytesfromtheinputstreambut

beforewritingthemtotheline,add:







//calculatelevel

calculateLevel(buffer,readPoint,leftover);



AsFlorianarguesinhismessage,theideaofalevelisupfor

interpretation,butthereisageneralsensethatitshould

representtheloudnessorquietnessoftheaudioatacertain

time.Makingtheproblemworseisthefactthatthesample

valueswillalwaysbegoingupanddownbecausethesamples

representhowmuchaspeakershouldbeexcitedorrelaxed,

andit'sthesample'speriodicchangethatcreatessoundwaves

wehear.Putanotherway,eventheloudestsoundscanhave

some0samplesatthebottomoftheirwaves.

Asacrudeattemptatapproximatingalevel,thishack's

implementationgetsthemaximumamplitude(oneither

speaker,ifthesourceisstereo)intheentirebuffer.Tomake

thisalittlemorefine-tuned,thisversionoftheplayerfigures

outabuffersizesuitabletoprovide1/20ofasecondofaudio,

ratherthantheflat32KBusedearlier.Todothat,addthisafter

gettingtheLineintheconstructor:



//figureoutasmallbuffersize



intbytesPerSec=format.getSampleSizeInBits()*

(int)format.getSampleRate();

System.out.println("bytesPerSec="+bytesPerSec);



intbufferSize=bytesPerSec/20;



buffer=newbyte[bufferSize];



Thisneedstosyncwiththelineaswelliftheline'sbufferis

nearlyfull,itwon'tacceptthisentirebufferonthewrite()

withoutblocking.So,youcantunethewhilelooptodoareadand-writeonlyiftheLinewillacceptabufferfulofdata.Dothis

byaddingthefollowingblockaftertheif(playing)statement:







//onlywriteifthelinewilltakeat

//leastabuffer-fulofdata













if(line.available()


Thread.yield();



continue;

}



Now,theonlyproblemisimplementingcalculateLevel()i.e.,doing

theactualiterationthroughthebuffertocalculateamaximum

value.This,frankly,isahugepaininthebutt,becauseto

determineeachsamplevalue,youhavetodealwithfourissues

youhadn'tcaredaboutbefore:

Channels(i.e.,monoversusstereo)

Samplesize

Endianness

Signing

ThisishandledinthecalculateLevel()methodof

PCMFilePlayerLeveler,listedinExample10-11.



Example10-11.Methodtocalculateacrude

"level"ofsamplebytesinabuffer

privatevoidcalculateLevel(byte[]buffer,









intreadPoint,









intleftOver){

intmax=0;

booleanuse16Bit=(format.getSampleSizeInBits()==16);

booleansigned=(format.getEncoding()==

AudioFormat.Encoding.PCM_SIGNED);



booleanbigEndian=(format.isBigEndian());

if(use16Bit){



for(inti=readPoint;i


intvalue=0;



//dealwithendianness



inthiByte=(bigEndian?buffer[i]:buffer[i+1]);



intloByte=(bigEndian?buffer[i+1]:buffer[i]);



if(signed){





shortshortVal=(short)hiByte;





shortVal=(short)((shortVal<<8)|(byte)





value=shortVal;



}else{





value=(hiByte<<8)|loByte;



}





max=Math.max(max,value);



}//for

}else{



//8bit-noendiannessissues,justsign



for(inti=readPoint;i




intvalue=0;





if(signed){





value=buffer[i];

}else{





shortshortVal=0;







shortVal=(short)(shortVal|buffer



value=shortVal;

}





max=Math.max(max,value);



}//for

}//8bit

//expressmaxasfloatof0.0to1.0ofmaxvalue

//of8or16bits(signedorunsigned)

if(signed){

if(use16Bit){level=(float)max/MAX_16_BITS_SIGNED



else{level=(float)max/MAX_8_BITS_SIGNED;}

}else{

if(use16Bit){level=(float)max/MAX_16_BITS_UNSIGN





else{level=(float)max/MAX_8_BITS_UNSIGNED;}

}

}//calculateLevel



Thiscrudeimplementationjustreadsallthesamplesinorder,

meaningthestereocasesamplesalternatingbetweenleftand

rightisignored.Thus,themaximumvaluewins,regardlessof

whatchannelit'son.

Figuringoutthevalueisstillabit-mungingpainbecauseofthe

threeoutstandingissuesthatmustbedealtwith.For16-bit

audio,thesamplesshouldbereadtwoatatime.Youarrange

the"high"(mostsignificant)and"low"(leastsignificant)bytes

basedontheendiannessoftheformat,andthencasttoaJava

intorshortbasedonwhetheryouneedtomaintainthesignbit

(ina32-bitint,the16bitswon'tbesigned;inJava's16-bit

short,thesignwillbemaintained).Eight-bitaudiosparesyou

theendiannesshassle,thoughyoustillhavetobeawareof

signage,andcasttoabyteorshortbasedonwhetheryouneed

topreserveasign.

Allofthis,justtofigureoutthevalueofasample.Asyoumight

expect,theonlythinglefttodooneachloopistocomparethe

sample'svaluetothemaximumforthisbuffer,andtoresetthe

maximumifthisvalueishigher.Attheend,youdividethe

maximumvalueagainstthemaximumpossiblevalueforthat

combinationofbitsandsignagetogetthelevelasavalue

between0.0and1.0.



10.9.4.RunningtheHackedHack

Whenyourunthishack,youfinallygetaplayerwithalevel

meter,asseeninFigure10-9.



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Hack 77. Show Audio Information While Playing SoundHack

Tải bản đầy đủ ngay(0 tr)

×