克服闪存波形性能问题的建议
我的问题是下面这个问题的后续问题,但我认为最好开始一个新问题。
我在 flash 中有一个波形绘制实现,它从 mp3 中提取 pcm 数据并以多个缩放级别绘制波形。它工作得很好,直到你开始处理超过几分钟的曲目,然后它就太慢了,令人无法接受。所以我正在寻求更快的解决方案。
我首先想到尝试使用像素弯曲器,因为我听说它的计算速度非常快。这是事实,但如上面发布的链接所示,缺乏 for 循环使得该技术不适合候选。
所以现在我正在考虑什么是钱包作为替代方案的最佳途径。
用 c 语言编写波形计算代码并使用 alchemy 会给我带来明显的速度和性能改进吗?它本质上仍然只是 as3 运行对吧?
使用某种服务器端脚本会更好吗?也许将 pcm 数据作为字节数组传递给 php 脚本并让脚本返回绘图数据?
你能想到我可以尝试研究的其他技术吗?
编辑:>>
我在 as3 中尝试做的是
将 mp3s 音频数据提取到字节数组 确定要在最大缩放级别绘制的虚拟像素数(例如,800 像素 = 2 倍缩放下轨道的 10 秒)
因此,如果我的样本数 = 33315840 并且最大虚拟像素数 = 1600(需要滚动,因为我的视口是 800 像素宽)那么我的块大小 = 33315840/1600 =20822
因此,对于 hte bytearray 中的每 20822 个样本,我找到最大值并将该值用作我的绘图值
编辑 2
@backtodos
我考虑了您提出的观点,它确实看起来比整个最大业务更明智。
我已经完成了以下代码,我认为它代表了您所讨论的内容。 一个问题是,如果我将分辨率更改为大于 800,我会得到非常高的线和非常短的线,这看起来很奇怪。 800 时它的波形非常好!
如果我的分辨率超过 1600,当我尝试绘制矩形时会抛出错误,指出参数无效。
我想我对你的缩放策略有点困惑。您说“如果放大,则会以重复的分辨率重新绘制波形”。 我真的不明白你的意思是什么?我知道我一次只会绘制 800 个像素。假设我进行了 2 倍缩放,这是否意味着我将样本源的读取量加倍并拥有 1600 个绘图点,但根据滚动位置一次只绘制 800 个绘图点?
private function calculatePlottingData():void
{
this._container=new UIComponent()
drawon=new MovieClip()
_container.addChild(drawon)
addChild(_container)
var spriteSize:Number;
spriteSize=800; // my resolution
//ba is a bytearray filled with my extracted audio data
//blocksize helps me determine where in the bytearray
//to read a sample value from for plotting
blocksize=Math.floor(ba.length / spriteSize);
var tstart:Number=getTimer()
var l:Number
var r:Number
var la:Number
var ra:Number
var readpoint:int=0;
var val:Number;
for (var i:int=0; i < spriteSize; i++)
{
readpoint=i * blocksize
ba.position=readpoint;
//read teh left and right sample
la=ba.readFloat();
ra=ba.readFloat();
val=(la + ra) / 2;
plottingvalues[i]=val
drawon.graphics.beginFill(0xff0000)
}
var tend:Number=getTimer() - tstart;
trace("time taken=" + tend / 1000)
draw()
}
private function draw():void
{
var val:Number
for (var i:int=0; i < _viewportwidth; i++)
{
drawon.graphics.beginFill(0xffffff)
val=plottingvalues[i];
drawon.graphics.drawRect(i, 50, 1, val * 50)
}
}
编辑 3 > 基于仅获取 800 个绘图点改进了代码 这段代码对于我的目的来说看起来足够快。使用 @backtodos 坚持解决方案的建议可以节省大量处理时间。
package drawing
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.Sound;
import flash.utils.ByteArray;
import flash.utils.getTimer;
import mx.core.Application;
public class WaveformView extends MovieClip
{
private var _sound:Sound
private var _resolution:Number
private var _quality:int=1;
private var _buffersize:int;
private var _ba:ByteArray=new ByteArray()
private var _step:Number
private var _zoomby:int=1;
private var _zoomfactor:int=2;
private var _blocksize:int;
private var _scrollbar:Bitmap
private var _scrollthumb:MovieClip
private var _relativeThumbPosition:Number=0
private var _sliderRange:int
private var _sliderCenterMinLeft:int
private var _sliderCenterMax_Right:int;
public var _slider_rel_pos:Number=0;
private var _sliderCenter:Number=0;
public function WaveformView(snd:Sound, res:Number=800, quality:int=2, buffersize:int=2048)
{
super();
this._sound=snd;
this._resolution=res;
this._quality=quality; // not implemented yet
this._buffersize=buffersize;
initScrollBar();
getPCM()
drawwaveform()
}
private function initScrollBar():void
{
var sbbmd:BitmapData=new BitmapData(_resolution, 20, false, 0xcccccc)
_scrollbar=new Bitmap(sbbmd)
_scrollbar.y=120
addChild(_scrollbar)
sbbmd=new BitmapData(_resolution, 16, false, 0xeeeeee)
_scrollthumb=new MovieClip()
_scrollthumb.graphics.beginFill(0xff0000)
_scrollthumb.graphics.drawRect(0, 0, _resolution, 10)
_scrollthumb.graphics.endFill()
_scrollthumb.y=125
addChild(_scrollthumb)
_scrollthumb.buttonMode=true
_scrollthumb.addEventListener(MouseEvent.MOUSE_DOWN, beginthumbdrag)
}
private function beginthumbdrag(e:MouseEvent):void
{
_scrollthumb.startDrag(false, new Rectangle(_scrollbar.x, _scrollbar.y + 5, _scrollbar.width - (_scrollthumb.width), 0))
_scrollthumb.addEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving, false, 0, true);
_scrollthumb.addEventListener(MouseEvent.MOUSE_UP, endthumbdrag, false, 0, true);
}
private function endthumbdrag(e:MouseEvent):void
{
_scrollthumb.stopDrag();
e.updateAfterEvent();
_scrollthumb.removeEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving);
}
private function shuttleMoving(e:MouseEvent):void
{
calculateShuttleRelativePosition();
drawwaveform()
e.updateAfterEvent()
}
private function calculateShuttleRelativePosition():void
{
var _x:Number=_scrollthumb.x
_sliderCenter=_x + (_scrollthumb.width / 2)
_sliderRange=_scrollbar.width - _scrollthumb.width;
_slider_rel_pos=(_sliderCenter - _sliderCenterMinLeft) / _sliderRange
}
public function getPCM():void
{
var len:int=_sound.length * 44.1
_sound.extract(_ba, len)
this._blocksize=_ba.length / (_resolution * _zoomby)
shaveblocksize()
}
public function zoomin():void
{
if (this._zoomby < 16)
{
this._zoomby*=2
resizeThumb()
drawwaveform()
}
}
public function zoomout():void
{
if (_zoomby >= 2)
{
this._zoomby/=2
resizeThumb()
drawwaveform()
}
}
private function resizeThumb():void
{
_scrollthumb.width=_scrollbar.width / _zoomby
if (_scrollthumb.width == _resolution)
{
_slider_rel_pos=0
}
_sliderCenterMinLeft=_scrollthumb.width / 2;
_sliderCenterMax_Right=_scrollbar.width - (_scrollthumb.width / 2);
_sliderRange=_scrollbar.width - _scrollthumb.width;
_sliderRange=_scrollbar.width - _scrollthumb.width;
_scrollthumb.x=(_slider_rel_pos * _sliderRange)
}
public function drawwaveform():void
{
var starttime:Number=getTimer()
var readposition:int
var l:Number
var r:Number
var p:Number
this._blocksize=_ba.length / (_resolution * _zoomby)
shaveblocksize()
this.graphics.clear()
this.graphics.beginFill(0xc5c5c5, 0.5)
this.graphics.drawRect(0, 50, _resolution, 1)
var max:int=_ba.length - (_blocksize * _resolution)
var pos:int=Formulas.interpolate(_slider_rel_pos, 0, max)
_ba.position=pos
for (var i:int=0; i < _resolution; i++)
{
l=Math.abs(_ba.readFloat());
r=Math.abs(_ba.readFloat());
l*=50
r*=50
p=(l + r)/2
try
{
this.graphics.drawRect(i, 50, 1, p)
this.graphics.drawRect(i, 50, 1, -p)
}
catch (e:Error)
{
}
_ba.position+=_blocksize
}
}
private function shaveblocksize():void
{
do
{
_blocksize--
} while (_blocksize % 4);
}
}
}
解决方案 感谢 backtodos 的帮助。这可能需要大量的优化,但对于某些人来说这是一个很好的起点。
package drawing
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.Sound;
import flash.utils.ByteArray;
import flash.utils.getTimer;
public class WaveformView extends MovieClip
{
private var _sound:Sound
private var _resolution:Number
private var _quality:int=1;
private var _buffersize:int;
private var _ba:ByteArray=new ByteArray()
private var _zoomby:int=1;
private var _zoomXFactor:int=2;
private var _maximumSecondsPerViewPort:int=10;
;
private var _blocksize:int;
private var _scrollbar:Bitmap
private var _scrollthumb:MovieClip
private var _relativeThumbPosition:Number=0
private var _sliderRange:int
private var _sliderCenterMinLeft:int
private var _sliderCenterMax_Right:int;
public var _slider_rel_pos:Number=0;
private var _sliderCenter:Number=0;
private var _resolutions:Array
private var _zoomsInSecondsArray:Array=new Array();
private var _numberOfZoomLevels:int;
public function WaveformView(snd:Sound, res:Number=800, quality:int=2, buffersize:int=2048, height:int=100)
{
super();
this._sound=snd;
this._resolution=res;
this._quality=quality; // not implemented yet
this._buffersize=buffersize;
// addChild(viewportBitmap)
_resolutions=new Array()
initScrollBar();
getPCM()
calculateZoomLevelData()
drawwaveform()
}
public function calculateZoomLevelData():void
{
_zoomsInSecondsArray=[]
var amt:int=Math.round(_sound.length / 1000);
_zoomsInSecondsArray.push(amt);
while (amt >= _maximumSecondsPerViewPort)
{
amt=amt / _zoomXFactor;
if (amt >= _maximumSecondsPerViewPort)
{
_zoomsInSecondsArray.push(amt);
}
}
_numberOfZoomLevels=_zoomsInSecondsArray.length;
var checkSize:int=_resolution;
var r:ResolutionCache
r=new ResolutionCache(checkSize)
_resolutions.push(r)
for (var c:int=1; c < _numberOfZoomLevels + 1; c++)
{
checkSize=checkSize * _zoomXFactor;
r=new ResolutionCache(checkSize)
_resolutions.push(r)
}
_resolutions.pop()
}
private function initScrollBar():void
{
var sbbmd:BitmapData=new BitmapData(_resolution, 20, false, 0xcccccc)
_scrollbar=new Bitmap(sbbmd)
_scrollbar.y=120
addChild(_scrollbar)
sbbmd=new BitmapData(_resolution, 16, false, 0xeeeeee)
_scrollthumb=new MovieClip()
_scrollthumb.graphics.beginFill(0xff0000)
_scrollthumb.graphics.drawRect(0, 0, _resolution, 10)
_scrollthumb.graphics.endFill()
_scrollthumb.y=125
addChild(_scrollthumb)
_scrollthumb.buttonMode=true
_scrollthumb.addEventListener(MouseEvent.MOUSE_DOWN, beginthumbdrag)
}
private function beginthumbdrag(e:MouseEvent):void
{
_scrollthumb.startDrag(false, new Rectangle(_scrollbar.x, _scrollbar.y + 5, _scrollbar.width - (_scrollthumb.width), 0))
_scrollthumb.addEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving, false, 0, true);
_scrollthumb.addEventListener(MouseEvent.MOUSE_UP, endthumbdrag, false, 0, true);
}
private function endthumbdrag(e:MouseEvent):void
{
_scrollthumb.stopDrag();
e.updateAfterEvent();
_scrollthumb.removeEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving);
}
private function shuttleMoving(e:MouseEvent):void
{
calculateShuttleRelativePosition();
drawwaveform()
//e.updateAfterEvent()
}
private function calculateShuttleRelativePosition():void
{
var _x:Number=_scrollthumb.x
_sliderCenter=_x + (_scrollthumb.width / 2)
_sliderRange=_scrollbar.width - _scrollthumb.width;
_slider_rel_pos=(_sliderCenter - _sliderCenterMinLeft) / _sliderRange
}
public function getPCM():void
{
var len:int=_sound.length * 44.1
_sound.extract(_ba, len)
this._blocksize=_ba.length / (_resolution * _zoomby)
shaveblocksize()
}
public function zoomin():void
{
if (this._zoomby < 16)
{
this._zoomby*=2
resizeThumb()
drawwaveform()
}
}
public function zoomout():void
{
if (_zoomby >= 2)
{
this._zoomby/=2
resizeThumb()
drawwaveform()
}
}
private function resizeThumb():void
{
_scrollthumb.width=_scrollbar.width / _zoomby
if (_scrollthumb.width == _resolution)
{
_slider_rel_pos=0
}
_sliderCenterMinLeft=_scrollthumb.width / 2;
_sliderCenterMax_Right=_scrollbar.width - (_scrollthumb.width / 2);
_sliderRange=_scrollbar.width - _scrollthumb.width;
_sliderRange=_scrollbar.width - _scrollthumb.width;
_scrollthumb.x=(_slider_rel_pos * _sliderRange)
}
private function getResolutionCache(vww:int):ResolutionCache
{
var r:ResolutionCache
for (var i:int=0; i < _resolutions.length; ++i)
{
if (_resolutions[i].name == vww)
{
r=_resolutions[i];
break;
}
}
return r;
}
public function drawwaveform():void
{
var starttime:Number=getTimer()
var readposition:int
var viewPortWidth:int=_resolution;
var virtualWindowWidth:int=_resolution * _zoomby;
var l:Number
var r:Number
var p:Number
var rc:ResolutionCache=getResolutionCache(virtualWindowWidth)
this._blocksize=_ba.length / virtualWindowWidth
shaveblocksize()
this.graphics.clear()
this.graphics.beginFill(0xc5c5c5, 0.5)
this.graphics.drawRect(0, 50, _resolution, 1)
var pixel:int=(_slider_rel_pos * (virtualWindowWidth - viewPortWidth - 1));
var readpoint:int=pixel * _blocksize;
var rect:Rectangle
_ba.position=readpoint;
this.graphics.clear()
this.graphics.beginFill(0xffffff)
for (var i:int=0; i < _resolution; i++)
{
if (rc.points[pixel] == -1)
{
_ba.position=pixel * _blocksize
l=_ba.readFloat() * 100;
r=_ba.readFloat() * 100;
p=(l + r) / 2
rc.addPoint(pixel, i, height / 2, p)
rect=rc.points[pixel]
//this.graphics.drawRect(rect.x,rect.y,rect.width,rect.height)
this.graphics.drawRect(i, 50, 1, p)
this.graphics.drawRect(i, 50, 1, -p)
}
else
{
//viewportBitmapdata.fillRect(rc.points[pixel], 0xffffff)
rect=rc.points[pixel]
this.graphics.drawRect(i, 50, 1, rect.height)
this.graphics.drawRect(i, 50, 1, -rect.height)
//this.graphics.drawRect(i,height/2,rect.width,rect.height)
}
pixel++
}
}
private function shaveblocksize():void
{
do
{
_blocksize--
} while (_blocksize % 4);
}
}
}
My question is sort of a follow on from this question below but I thought it'd be best to start a new question.
help converting this as3 code to pixel bender code
So I have a waveform drawing implementation in flash that extracts pcm data from an mp3 and draws a waveform at multiple zoom levels. It works fine until you begin dealing with tracks longer than a couple of minutes and then it is just unacceptedly too slow. So I'm after a faster solution.
I first thought to try and use pixel bender because I heard it is very quick at calculations. This is true but as illustrated in the link posted above the lack of for loops makes the technology an unsuitable candidate.
So now I'm thinking what would be the best avenue too purse as an alternative.
Would writing my waveform calculation code in c and using alchemy give me a perceiveable speed and performance improvement? Its essentially still just as3 running right?
Would I be better off using some sort of server sider script? Maybe passing the pcm data as a bytearray to a php script and getting the script to return the plotting data?
Can you think of any other techniques that I could try to look into to?
EDIT:>>
What I try to do in as3 is
Extract a mp3s audio data to a byte array
Determine the virtual number of pixels to plot at maximum zoom level (800 pixels = 10 seconds of a track at 2x zoom for example)
So if my number of samples = 33315840 and the maximum number of virtual pixels = 1600 (scrolling required because my viewport is 800 pixels wide) then my chunk size =
33315840/1600 =20822
So for every 20822 samples in hte bytearray I find the maximum value and use that value as my plotting value
EDIT 2
@backtodos
I had a think about the points you where making and it does seem wiser than this whole max business.
I've done up the following code which I think represents what you were talking about.
One problem is that if I change my resolution to anything greater than 800 I get very tall line and very short ones which looks very odd. At 800 its a very nice waveform!
If my resolution exceeds 1600 I get errors thrown when I ttry to draw the rect saying that a parameter was invalid.
I think I'm a little confused about you zooming strategy. You say "If you zoom in, you redraw the wave form with a duplicate resolution".
I don't really get what you mean my that? I know I will only ever be drawing 800 pixels at a time. Lets say I do a 2x zoom, would that mean that I double the amount of reads from by sample source and have 1600 plotting points but only draw 800 of them at a time based on the scroll position?
private function calculatePlottingData():void
{
this._container=new UIComponent()
drawon=new MovieClip()
_container.addChild(drawon)
addChild(_container)
var spriteSize:Number;
spriteSize=800; // my resolution
//ba is a bytearray filled with my extracted audio data
//blocksize helps me determine where in the bytearray
//to read a sample value from for plotting
blocksize=Math.floor(ba.length / spriteSize);
var tstart:Number=getTimer()
var l:Number
var r:Number
var la:Number
var ra:Number
var readpoint:int=0;
var val:Number;
for (var i:int=0; i < spriteSize; i++)
{
readpoint=i * blocksize
ba.position=readpoint;
//read teh left and right sample
la=ba.readFloat();
ra=ba.readFloat();
val=(la + ra) / 2;
plottingvalues[i]=val
drawon.graphics.beginFill(0xff0000)
}
var tend:Number=getTimer() - tstart;
trace("time taken=" + tend / 1000)
draw()
}
private function draw():void
{
var val:Number
for (var i:int=0; i < _viewportwidth; i++)
{
drawon.graphics.beginFill(0xffffff)
val=plottingvalues[i];
drawon.graphics.drawRect(i, 50, 1, val * 50)
}
}
EDIT 3 > Improved code based on getting only ever getting 800 plotting points
this code is looking to be quick enough for my purposes. It saves a lot of processing using @backtodos's suggestion of sticking to a resolution.
package drawing
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.Sound;
import flash.utils.ByteArray;
import flash.utils.getTimer;
import mx.core.Application;
public class WaveformView extends MovieClip
{
private var _sound:Sound
private var _resolution:Number
private var _quality:int=1;
private var _buffersize:int;
private var _ba:ByteArray=new ByteArray()
private var _step:Number
private var _zoomby:int=1;
private var _zoomfactor:int=2;
private var _blocksize:int;
private var _scrollbar:Bitmap
private var _scrollthumb:MovieClip
private var _relativeThumbPosition:Number=0
private var _sliderRange:int
private var _sliderCenterMinLeft:int
private var _sliderCenterMax_Right:int;
public var _slider_rel_pos:Number=0;
private var _sliderCenter:Number=0;
public function WaveformView(snd:Sound, res:Number=800, quality:int=2, buffersize:int=2048)
{
super();
this._sound=snd;
this._resolution=res;
this._quality=quality; // not implemented yet
this._buffersize=buffersize;
initScrollBar();
getPCM()
drawwaveform()
}
private function initScrollBar():void
{
var sbbmd:BitmapData=new BitmapData(_resolution, 20, false, 0xcccccc)
_scrollbar=new Bitmap(sbbmd)
_scrollbar.y=120
addChild(_scrollbar)
sbbmd=new BitmapData(_resolution, 16, false, 0xeeeeee)
_scrollthumb=new MovieClip()
_scrollthumb.graphics.beginFill(0xff0000)
_scrollthumb.graphics.drawRect(0, 0, _resolution, 10)
_scrollthumb.graphics.endFill()
_scrollthumb.y=125
addChild(_scrollthumb)
_scrollthumb.buttonMode=true
_scrollthumb.addEventListener(MouseEvent.MOUSE_DOWN, beginthumbdrag)
}
private function beginthumbdrag(e:MouseEvent):void
{
_scrollthumb.startDrag(false, new Rectangle(_scrollbar.x, _scrollbar.y + 5, _scrollbar.width - (_scrollthumb.width), 0))
_scrollthumb.addEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving, false, 0, true);
_scrollthumb.addEventListener(MouseEvent.MOUSE_UP, endthumbdrag, false, 0, true);
}
private function endthumbdrag(e:MouseEvent):void
{
_scrollthumb.stopDrag();
e.updateAfterEvent();
_scrollthumb.removeEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving);
}
private function shuttleMoving(e:MouseEvent):void
{
calculateShuttleRelativePosition();
drawwaveform()
e.updateAfterEvent()
}
private function calculateShuttleRelativePosition():void
{
var _x:Number=_scrollthumb.x
_sliderCenter=_x + (_scrollthumb.width / 2)
_sliderRange=_scrollbar.width - _scrollthumb.width;
_slider_rel_pos=(_sliderCenter - _sliderCenterMinLeft) / _sliderRange
}
public function getPCM():void
{
var len:int=_sound.length * 44.1
_sound.extract(_ba, len)
this._blocksize=_ba.length / (_resolution * _zoomby)
shaveblocksize()
}
public function zoomin():void
{
if (this._zoomby < 16)
{
this._zoomby*=2
resizeThumb()
drawwaveform()
}
}
public function zoomout():void
{
if (_zoomby >= 2)
{
this._zoomby/=2
resizeThumb()
drawwaveform()
}
}
private function resizeThumb():void
{
_scrollthumb.width=_scrollbar.width / _zoomby
if (_scrollthumb.width == _resolution)
{
_slider_rel_pos=0
}
_sliderCenterMinLeft=_scrollthumb.width / 2;
_sliderCenterMax_Right=_scrollbar.width - (_scrollthumb.width / 2);
_sliderRange=_scrollbar.width - _scrollthumb.width;
_sliderRange=_scrollbar.width - _scrollthumb.width;
_scrollthumb.x=(_slider_rel_pos * _sliderRange)
}
public function drawwaveform():void
{
var starttime:Number=getTimer()
var readposition:int
var l:Number
var r:Number
var p:Number
this._blocksize=_ba.length / (_resolution * _zoomby)
shaveblocksize()
this.graphics.clear()
this.graphics.beginFill(0xc5c5c5, 0.5)
this.graphics.drawRect(0, 50, _resolution, 1)
var max:int=_ba.length - (_blocksize * _resolution)
var pos:int=Formulas.interpolate(_slider_rel_pos, 0, max)
_ba.position=pos
for (var i:int=0; i < _resolution; i++)
{
l=Math.abs(_ba.readFloat());
r=Math.abs(_ba.readFloat());
l*=50
r*=50
p=(l + r)/2
try
{
this.graphics.drawRect(i, 50, 1, p)
this.graphics.drawRect(i, 50, 1, -p)
}
catch (e:Error)
{
}
_ba.position+=_blocksize
}
}
private function shaveblocksize():void
{
do
{
_blocksize--
} while (_blocksize % 4);
}
}
}
Solution
Thanks to backtodos for his help. This could do with a hell of a lot of optimization but its good starting point for somebody.
package drawing
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.media.Sound;
import flash.utils.ByteArray;
import flash.utils.getTimer;
public class WaveformView extends MovieClip
{
private var _sound:Sound
private var _resolution:Number
private var _quality:int=1;
private var _buffersize:int;
private var _ba:ByteArray=new ByteArray()
private var _zoomby:int=1;
private var _zoomXFactor:int=2;
private var _maximumSecondsPerViewPort:int=10;
;
private var _blocksize:int;
private var _scrollbar:Bitmap
private var _scrollthumb:MovieClip
private var _relativeThumbPosition:Number=0
private var _sliderRange:int
private var _sliderCenterMinLeft:int
private var _sliderCenterMax_Right:int;
public var _slider_rel_pos:Number=0;
private var _sliderCenter:Number=0;
private var _resolutions:Array
private var _zoomsInSecondsArray:Array=new Array();
private var _numberOfZoomLevels:int;
public function WaveformView(snd:Sound, res:Number=800, quality:int=2, buffersize:int=2048, height:int=100)
{
super();
this._sound=snd;
this._resolution=res;
this._quality=quality; // not implemented yet
this._buffersize=buffersize;
// addChild(viewportBitmap)
_resolutions=new Array()
initScrollBar();
getPCM()
calculateZoomLevelData()
drawwaveform()
}
public function calculateZoomLevelData():void
{
_zoomsInSecondsArray=[]
var amt:int=Math.round(_sound.length / 1000);
_zoomsInSecondsArray.push(amt);
while (amt >= _maximumSecondsPerViewPort)
{
amt=amt / _zoomXFactor;
if (amt >= _maximumSecondsPerViewPort)
{
_zoomsInSecondsArray.push(amt);
}
}
_numberOfZoomLevels=_zoomsInSecondsArray.length;
var checkSize:int=_resolution;
var r:ResolutionCache
r=new ResolutionCache(checkSize)
_resolutions.push(r)
for (var c:int=1; c < _numberOfZoomLevels + 1; c++)
{
checkSize=checkSize * _zoomXFactor;
r=new ResolutionCache(checkSize)
_resolutions.push(r)
}
_resolutions.pop()
}
private function initScrollBar():void
{
var sbbmd:BitmapData=new BitmapData(_resolution, 20, false, 0xcccccc)
_scrollbar=new Bitmap(sbbmd)
_scrollbar.y=120
addChild(_scrollbar)
sbbmd=new BitmapData(_resolution, 16, false, 0xeeeeee)
_scrollthumb=new MovieClip()
_scrollthumb.graphics.beginFill(0xff0000)
_scrollthumb.graphics.drawRect(0, 0, _resolution, 10)
_scrollthumb.graphics.endFill()
_scrollthumb.y=125
addChild(_scrollthumb)
_scrollthumb.buttonMode=true
_scrollthumb.addEventListener(MouseEvent.MOUSE_DOWN, beginthumbdrag)
}
private function beginthumbdrag(e:MouseEvent):void
{
_scrollthumb.startDrag(false, new Rectangle(_scrollbar.x, _scrollbar.y + 5, _scrollbar.width - (_scrollthumb.width), 0))
_scrollthumb.addEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving, false, 0, true);
_scrollthumb.addEventListener(MouseEvent.MOUSE_UP, endthumbdrag, false, 0, true);
}
private function endthumbdrag(e:MouseEvent):void
{
_scrollthumb.stopDrag();
e.updateAfterEvent();
_scrollthumb.removeEventListener(MouseEvent.MOUSE_MOVE, shuttleMoving);
}
private function shuttleMoving(e:MouseEvent):void
{
calculateShuttleRelativePosition();
drawwaveform()
//e.updateAfterEvent()
}
private function calculateShuttleRelativePosition():void
{
var _x:Number=_scrollthumb.x
_sliderCenter=_x + (_scrollthumb.width / 2)
_sliderRange=_scrollbar.width - _scrollthumb.width;
_slider_rel_pos=(_sliderCenter - _sliderCenterMinLeft) / _sliderRange
}
public function getPCM():void
{
var len:int=_sound.length * 44.1
_sound.extract(_ba, len)
this._blocksize=_ba.length / (_resolution * _zoomby)
shaveblocksize()
}
public function zoomin():void
{
if (this._zoomby < 16)
{
this._zoomby*=2
resizeThumb()
drawwaveform()
}
}
public function zoomout():void
{
if (_zoomby >= 2)
{
this._zoomby/=2
resizeThumb()
drawwaveform()
}
}
private function resizeThumb():void
{
_scrollthumb.width=_scrollbar.width / _zoomby
if (_scrollthumb.width == _resolution)
{
_slider_rel_pos=0
}
_sliderCenterMinLeft=_scrollthumb.width / 2;
_sliderCenterMax_Right=_scrollbar.width - (_scrollthumb.width / 2);
_sliderRange=_scrollbar.width - _scrollthumb.width;
_sliderRange=_scrollbar.width - _scrollthumb.width;
_scrollthumb.x=(_slider_rel_pos * _sliderRange)
}
private function getResolutionCache(vww:int):ResolutionCache
{
var r:ResolutionCache
for (var i:int=0; i < _resolutions.length; ++i)
{
if (_resolutions[i].name == vww)
{
r=_resolutions[i];
break;
}
}
return r;
}
public function drawwaveform():void
{
var starttime:Number=getTimer()
var readposition:int
var viewPortWidth:int=_resolution;
var virtualWindowWidth:int=_resolution * _zoomby;
var l:Number
var r:Number
var p:Number
var rc:ResolutionCache=getResolutionCache(virtualWindowWidth)
this._blocksize=_ba.length / virtualWindowWidth
shaveblocksize()
this.graphics.clear()
this.graphics.beginFill(0xc5c5c5, 0.5)
this.graphics.drawRect(0, 50, _resolution, 1)
var pixel:int=(_slider_rel_pos * (virtualWindowWidth - viewPortWidth - 1));
var readpoint:int=pixel * _blocksize;
var rect:Rectangle
_ba.position=readpoint;
this.graphics.clear()
this.graphics.beginFill(0xffffff)
for (var i:int=0; i < _resolution; i++)
{
if (rc.points[pixel] == -1)
{
_ba.position=pixel * _blocksize
l=_ba.readFloat() * 100;
r=_ba.readFloat() * 100;
p=(l + r) / 2
rc.addPoint(pixel, i, height / 2, p)
rect=rc.points[pixel]
//this.graphics.drawRect(rect.x,rect.y,rect.width,rect.height)
this.graphics.drawRect(i, 50, 1, p)
this.graphics.drawRect(i, 50, 1, -p)
}
else
{
//viewportBitmapdata.fillRect(rc.points[pixel], 0xffffff)
rect=rc.points[pixel]
this.graphics.drawRect(i, 50, 1, rect.height)
this.graphics.drawRect(i, 50, 1, -rect.height)
//this.graphics.drawRect(i,height/2,rect.width,rect.height)
}
pixel++
}
}
private function shaveblocksize():void
{
do
{
_blocksize--
} while (_blocksize % 4);
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
到底为什么你要尝试在某些块上找到最大值?
另外,这段代码应该做什么:
ml 和 mr 始终为 0,那么检查它有什么意义呢? divCount 是什么?
如果是为了绘制波形,我会以一定的分辨率来绘制它。如果我想在 500 像素宽的区域上绘制波形,基本上 500 个样本就可以完成这项工作。如果你想让它平均一点,那么每个像素一手的样本就足够了。
因此,从像素表示的时间范围内,选择 4 个样本(等距或随机)并计算平均值。如果方差超过某个阈值,您可以尝试通过获取更多样本来获得更可靠的值。一般来说,波形是连续且平滑的。绘制图表的时间应与图表的大小成线性比例,而不是与所涉及的样本数量成线性比例。
问候
后退2dos
Why exactly is it, that you try to find maximum over some chunks?
Also, what is this code supposed to do:
ml and mr are always 0, so what is the point of checking that? and what is divCount?
If it is for drawing a waveform, I'd draw it with a certain resolution. If I want to draw a waveform on an area 500 pixels wide, than basically 500 samples will do the job. If you want it to average out a little, than a hand full of samples per pixel really should be enough.
So from the time frame represented by a pixel, you choose 4 samples (equidistant or random) and calculate the average. Should the variance exceed a certain treshold, you could try to get a more reliable value by getting more samples. In general, waveforms are continuous and smooth. The time to draw the graph should scale linearly with the size of the graph, not with the number of samples involved.
greetz
back2dos