实现卡拉OK效果(300分)

  • 主题发起人 主题发起人 匪匪
  • 开始时间 开始时间

匪匪

Unregistered / Unconfirmed
GUEST, unregistred user!
程序主要表现形式:播放一首MP3音乐,同时将标签进行变色处理
因为播放规则会不一样,比如A句播放一遍,再停100ms,B句播放两遍,再停10ms,所以我将这些规则全部放入一个Tlist中预先存起来。然后在一个线程中,从这个TList中取出操作,解析后,再运行。可能有人会说你干嘛一定要用线程来画界面呢,我是没办法的办法,目前是这样子做的,如果有更好的方法,也无法改了,只能用线程实现了。
现在程序的问题是:当播放一首音乐的时候(同时标签正在变色过程中),我点击另一句C,同时开始播放C句对应的音乐,当点击过快时,以前一句的色彩来不及清除。具体代码如下:
procedure TChThread.Execute;
begin
try
Coinitialize(nil);
while not Terminateddo
begin
WaitForSingleObject(EventHandle, INFINITE);
ResetEvent(EventHandle);
if not Box.FStopAction then
begin
Sleep(0);
Action;   //真正执行的代码在此
Sleep(0);
end;
if Terminated then
begin
Box.PressStop := True;
Box.FStopAction := True;
Break;
end;
end;
//等待主线程激活该事件
WaitForSingleObject(CloseEvent, THREADWAITTIME);
//线程执行完毕后,激活终止事件
SetEvent(WaitEvent);

finally
CoUninitialize;
end;
end;

--------------------------------------
procedure TChThread.Action;
begin
Box.ThreadAction;//这里是运行控件中的一个函数。
end;
---------------------------------------
procedure TCBox.ThreadAction;
var
i: Integer;
begin
//依据操作记录队列,从中取出相关的操作记录,并依据操作类型,生成不同的操作
for i:= 0 to ActionList.Count - 1do
begin
if not FStopAction then
begin
case pRecordAction(ActionList.Items)^.aStyle of
aPlay: Play(pPlayAction(ActionList.Items));
aPause: Pause(pPauseAction(ActionList.Items));
end;
end;
end;
end;

---------------------------------------
procedure TCBox.Play(action: pPlayAction);
var
i,j: Integer;
ObjectList: TList;
AudioName: string;
begin
if action^.aStyle = aPlay then
begin
ObjectList:= nil;
//找到相关控件组(句子与图片可能有几个对应的)
FoundContrl(action^.oStyle, action^.Id, ObjectList);
if (Assigned(ObjectList)) and (ObjectList.Count >0) then
begin
AudioName:= '';
AudioName:= XML.GetAudio(action^.oStyle, Action^.Id);
if AudioName <> '' then
begin
for i:= 0 to action^.Num - 1do
if FStopAction then
begin
//MediaPlay.Stop;
Break;
end
else

ChangeColor(ObjectList, AudioName);//标签变色的代码就在此中
end;
end;
end;

end;

--------------------------------------
//-------------------------变色----------------------------//
//函数功能: //
// 以句为单位变色,同时播放该句对应的音乐 //
//---------------------------------------------------------//
procedure TCBox.ChangeColor(lObjectList: TList;
AudioName: string);
var
i: Integer;
strAudioName: string;
SI: TScrollInfo;
begin
strAudioName:= FAudioPath + AudioName;
ChangeColorLength := 0;
//判断是否需要滚动条下移
SI.cbSize := SizeOf(TScrollInfo);
SI.fMask := SIF_ALL;
GetScrollInfo(Self.Handle, SB_VERT, SI);
if TLabel(pPraseObject(lObjectList.Items[0])^.oRecord).Top + SI.nPos +
TLabel(pPraseObject(lObjectList.Items[0])^.oRecord).Height >= SI.nPage + SI.nPos then
begin
SetScrollPositon(True);
end;

Mp3(strAudioName);
if IsChangeColor then
begin
//字体组变成红色
for i:= 0 to lObjectList.Count - 1do
if TLabel(pPraseObject(lObjectList.Items)^.oRecord).Visible then
begin
TLabel(pPraseObject(lObjectList.Items)^.oRecord).Font.Color:= clRed;
end;

Step := GetLabelStep;
mp3MinLeft := mp3Time;
mp3Start := GetTickCount;
//字体变色,从lObjectList中第一个元素开始,循环变色

mp3Pause := False;
try
for i:= 0 to lObjectList.Count - 1do
if TLabel(pPraseObject(lObjectList.Items)^.oRecord).Visible then
begin
if (not FStopAction) then
begin
ChangeLabelColor(TLabel(pPraseObject(lObjectList.Items)^.oRecord));
TLabel(pPraseObject(lObjectList.Items)^.oRecord).Font.Color := clBlack;
end;
end;

except
StopMultimedia('Hjj');
CloseMultimedia('Hjj');
end;

//还原色彩为黑色
if (not FStopAction) then
if Assigned(lObjectList) then
begin
for i:= 0 to lObjectList.Count - 1do
if TLabel(pPraseObject(lObjectList.Items)^.oRecord).Visible then
begin
TLabel(pPraseObject(lObjectList.Items)^.oRecord).Font.Color:= clBlack;
end;
end;
end;
if not FStopAction then
Sleep(mp3SleepTime + 20);
StopMultimedia('Hjj');
CloseMultimedia('Hjj');
end;

上面的函数不说大家也知道了,变色效果跟卡拉OK一样,先是对应的句子变成红色,然后随着音乐的播放,前面的变成蓝色,至到所有的都变成蓝色,最后全部句子变成黑色,可是我的代码在随机快速的点击后,有时候蓝色没有清除。
-----------------------------------
该函数是对具体的某一个标签进行变色处理。包括了计算变色步进的进度值。
procedure TCBox.ChangeLabelColor(Labe: TLabel);
var
i, Height, Width, j: Integer;
rRect: TRect;
a1, a2: Integer;
strText: string;
FontName: TFontName;
FontStyle: TFontStyles;
FontSize: Integer;
Num: Integer;
//单词个数
ATime: Integer;
//每字符平均时间(ms)
TmpLabel: TLabel;
begin
try
try
strText := Labe.Caption;
Num := Length(strText);
FontName := Labe.Font.Name;
FontStyle := Labe.Font.Style;
FontSize := Labe.Font.Size;
Height:= Labe.Canvas.TextHeight(Caption);
Width:= Labe.Canvas.TextWidth(Trim(Caption));
//构造临时标签,求出每字符的长度
TmpLabel := TLabel.Create(Self);
TmpLabel.Font.Size := FontSize;
TmpLabel.Font.Style := FontStyle;
TmpLabel.Font.Name := FontName;
ATime := mp3Time div Num;
mp3SleepTime := mp3Time mod Num;
with Labedo
begin
i:= 0;
j:= 0;
//每次重画的长度
while (i <= Num)do
begin
TmpLabel.Caption := strText;
j := j + TmpLabel.Width;
if not FStopAction then
begin
//如果最后剩下的时间少于10ms,则一次性步进完毕
a1:= GetTickCount;
if (mp3Time - (a1 - mp3Start) <=2) and (mp3Time - (a1 - mp3Start) >0)then
begin
Canvas.Brush.Style:= bsClear;
Canvas.Font.Color:= clBlue;
rRect:= Rect(0, 0, Width, Height);
Canvas.Lock;
Canvas.TextRect(rRect, 0, 0, Caption);
Canvas.Unlock;
Break;
end
else
if (mp3Time - (a1 - mp3Start) > 2) then
begin
Canvas.Brush.Style:= bsClear;
Canvas.Font.Color:= clBlue;
rRect:= Rect(0, 0, j, Height);
Sleep(ATime);

Canvas.Lock;
Canvas.TextRect(rRect, 0, 0, Caption);
Canvas.Unlock;
end
else
Break;
i := i + 1;
end
else
begin
mp3Pause := True;
//代表人为结束
Break;
end;
end;
end;
except
TmpLabel.Free;
end;
finally
TmpLabel.Free;
end;
end;

希望高手来帮我解决此问题,只要问题能解决,可以再开300分的帖。
 
别外一点,在ThreadAction的for循环中也会出错,估计是ActionList里面的内容没有与主线程同步。因为我在主界面中点击别的功能后,会从新解析一次。
 
问题一:ThreadAction中for处会出错,提示ActionList越界,经判断是该列表没有进行同步。当进行另外一种操作时,这里面的值在主线程中被改变了,再次发生线程切换时,又回到以前的内容。此时就会出现越界。
问题二:当在标签上快速点击ObjectClick中,点击速度过快时,会让以前显示的句子的色还是蓝色,没有恢复成黑色(该代码我已经加入),估计也是一个线程同步的问题。标签变黑的代码没有运行到。
 
流的位置加消息
 
线程函数中,在弄与界面有关的操作时,没有加 上线程同步!
 
你就不要既在线程中操作,又在主程序中操作了,将所有你的操作放到一个队列中,一旦线程发现就马上执行,因为线程的切换还是很快的,所以用户绝对不会感觉到这个延迟,否则你用同步更麻烦.注意线程取队列命令只需要取第一个,取到后删除第一个,下次再取第二个(如果有的话)
 
to dey-999:
我的操作是有一个队列是:ActionList: TList;你说的方法取第一个,取后删除,再除剩下的。与for 是一样的吧。还有第一个问题怎么解决呢?就是即时的停止变色,且让色恢复过来。也就是我写的将标签变成黑色的句子运行。我之所以在变黑色ChangeColor前面加入一个if (not FStopAction) then
,是因为,在关闭的时候,如果变色会出错。
 
代码说明一下:
ChangeColor中的原理如下:
1、先发音
2、将这首音乐对应的句子先全部变红
3、然后随着音乐的播放,句子从左到右变成蓝色,ChangeLabelColor实现
4、当音乐播放完毕(变色完毕),或停止时,对应的所有句子全部变成黑色
现在问题是,在中途停止时(音乐不打断,播放完毕时没有问题),会出现变色不对,即快点的点击标签,调用ObjectClick后,会让句子变成蓝色,即步骤2未运行,同时4也没有运行,只有3在运行,即ChangeLabelColor运行了。
 
其实有相关的控件,这样太费劲了
 
现在改控件也来不及了啊。郁闷。
 
变色时间用的太长了
把那里的sleep变成多次判断,不要一次10,100ms的
要sleep(1)
在多次判断中,加入快速停止的判断
如果要快速停止,马上停止。
问题就基本解决了
 
改用小猪的方法后,那个即时点击的问题算是基本解决了,但第二个问题还存在。来人帮忙啊,急啊[:(]
 
没有人能帮我吗?能提出更好的设计方法的,我也给分啊。[:(]
 
多人接受答案了。
 
后退
顶部