📄 vc实现卡拉ok字幕叠加_3.htm
字号:
<TBODY>
<TR>
<TD align=middle><FONT
class=f22><B>VC实现卡拉OK字幕叠加</B></FONT></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD background=VC实现卡拉OK字幕叠加_3.files/newhard-040906_c_1.gif
height=1></TD></TR>
<TR>
<TD vAlign=top height=5></TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width=603 border=0>
<TBODY>
<TR>
<TD vAlign=top align=middle width=373 rowSpan=9>
<TABLE cellSpacing=0 cellPadding=0 width=373 align=center
border=0>
<TBODY>
<TR>
<TD align=middle><SPAN id=pip></SPAN></TD></TR>
<TR>
<TD align=middle><IFRAME
src="VC实现卡拉OK字幕叠加_3.files/y-soft-column-1.htm"
frameBorder=0 width=360 scrolling=no
height=50></IFRAME></TD></TR></TBODY></TABLE></TD>
<TD width=5></TD>
<TD vAlign=top width=220>
<TABLE cellSpacing=0 cellPadding=0 width=220 border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT class=f21><B>[文章信息]</B></FONT></TD></TR>
<TR>
<TD colSpan=2 height=10></TD></TR>
<TR>
<TD vAlign=top width=62>作者:</TD>
<TD vAlign=top width=158><FONT
color=#0e3e92>陆其明</FONT></TD></TR>
<TR>
<TD vAlign=top>时间:</TD>
<TD vAlign=top><FONT
color=#0e3e92>2003-11-21</FONT></TD></TR>
<TR>
<TD vAlign=top>出处:</TD>
<TD vAlign=top><FONT color=#0e3e92>VCHelp</FONT></TD></TR>
<TR>
<TD vAlign=top>责任编辑:</TD>
<TD vAlign=top><FONT color=#0e3e92>方舟</FONT></TD></TR>
<TR>
<TD colSpan=2 height=10></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=220 border=0>
<TBODY>
<TR>
<TD colSpan=2><FONT class=f21><B>[文章导读]</B></FONT></TD></TR>
<TR>
<TD colSpan=2 height=10></TD></TR>
<TR>
<TD vAlign=top colSpan=2><SPAN
class=f14>本文介绍了卡拉OK字幕叠加的一般原理以及VC上使用GDI的一种简单实现</SPAN></TD></TR>
<TR>
<TD colSpan=2 height=10></TD></TR></TBODY></TABLE>
<SCRIPT
language=JavaScript><!--var ad_id=1003//--></SCRIPT>
<SCRIPT language=JavaScript
src="VC实现卡拉OK字幕叠加_3.files/10.js"></SCRIPT>
</TD>
<TD width=5></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE></TD>
<TD width=1 bgColor=#b9bebe rowSpan=13></TD>
<TD vAlign=top align=middle width=170 bgColor=#ffffff rowSpan=13>
<TABLE cellSpacing=5 cellPadding=0 width=165 border=0>
<TBODY>
<TR>
<TD align=middle>
<SCRIPT language=JavaScript><!--var ad_id=0403//--></SCRIPT>
<SCRIPT language=JavaScript
src="VC实现卡拉OK字幕叠加_3.files/04.js"></SCRIPT>
</TD></TR></TBODY></TABLE>
<TABLE height=28 cellSpacing=0 cellPadding=0 width=160
background=VC实现卡拉OK字幕叠加_3.files/2004-30.gif border=0>
<TBODY>
<TR>
<TD align=middle><B>advertisement</B></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=1 bgColor=#adadad></TD>
<TD align=middle bgColor=#ffffff><SPAN id=sv></SPAN></TD>
<TD width=1 bgColor=#adadad></TD></TR></TBODY></TABLE>
<TABLE height=1 cellSpacing=0 cellPadding=0 width=160 bgColor=#adadad
border=0>
<TBODY>
<TR>
<TD></TD></TR></TBODY></TABLE>
<TABLE height=10 cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD></TD></TR></TBODY></TABLE>
<TABLE height=28 cellSpacing=0 cellPadding=0 width=160
background=VC实现卡拉OK字幕叠加_3.files/2004-30.gif border=0>
<TBODY>
<TR>
<TD align=middle><B>热点推荐</B></TD></TR></TBODY></TABLE>
<TABLE cellSpacing=0 cellPadding=0 width=160 border=0>
<TBODY>
<TR>
<TD width=1 bgColor=#adadad></TD>
<TD align=middle bgColor=#ffffff>
<TABLE cellSpacing=0 cellPadding=2 width=150 border=0>
<TBODY>
<TR>
<TD width=7>· </TD>
<TD width=143><A
href="http://www.yesky.com/SoftChannel/72339069014638592/20041017/1864973.shtml"
target=_blank>天极网软件频道改版调查</A></TD></TR>
<TR>
<TD width=7>· </TD>
<TD width=143><A
href="http://www.yesky.com/SoftChannel/72339069014638592/20041028/1869661.shtml"
target=_blank>10.28软件精选 找乐还是赌钱</A></TD></TR>
<TR>
<TD width=7>· </TD>
<TD width=143><A
href="http://www.yesky.com/SoftChannel/72342389024358400/20041028/1869618.shtml"
target=_blank>中国游戏业——被人们忽视的黑暗</A></TD></TR>
<TR>
<TD width=7>· </TD>
<TD width=143><A
href="http://www.yesky.com/SoftChannel/72356695560421376/20041028/1869602.shtml"
target=_blank>Windows 2000密码破解不完全指南</A></TD></TR>
<TR>
<TD width=7>· </TD>
<TD width=143><A
href="http://www.yesky.com/SoftChannel/72350085605752832/20041028/1869596.shtml"
target=_blank>解决XP SP2带来的网络故障(上)</A></TD></TR></TBODY></TABLE></TD>
<TD width=1 bgColor=#adadad></TD></TR></TBODY></TABLE>
<TABLE height=1 cellSpacing=0 cellPadding=0 width=160 bgColor=#adadad
border=0>
<TBODY>
<TR>
<TD></TD></TR></TBODY></TABLE></TD>
<TD width=1 bgColor=#b9bebe rowSpan=13></TD></TR>
<TR>
<TD vAlign=top bgColor=#f0f2fb height=5></TD></TR>
<TR>
<TD vAlign=top background=VC实现卡拉OK字幕叠加_3.files/newhard-040906_c_1.gif
bgColor=#f0f2fb height=1></TD></TR>
<TR>
<TD vAlign=top bgColor=#dee2f5>
<TABLE height=25 cellSpacing=0 cellPadding=0 width=590 align=center
border=0>
<TBODY>
<TR>
<TD width=90><FONT class=f21><B>[正文]</B></FONT></TD>
<TD align=right width=480>
<P align=right><FONT color=red><A
href="http://www.yesky.com/SoftChannel/72342371928702976/20031121/1746826_1.shtml">上一页</A> <A
href="http://www.yesky.com/SoftChannel/72342371928702976/20031121/1746826.shtml">1</A>
<A
href="http://www.yesky.com/SoftChannel/72342371928702976/20031121/1746826_1.shtml">2</A>
3 <A
href="http://www.yesky.com/SoftChannel/72342371928702976/20031121/1746826_3.shtml">4</A>
<A
href="http://www.yesky.com/SoftChannel/72342371928702976/20031121/1746826_3.shtml">下一页</A></FONT></P></TD>
<TD align=middle width=20></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD vAlign=top background=VC实现卡拉OK字幕叠加_3.files/newhard-040906_c_1.gif
bgColor=#f0f2fb height=1></TD></TR>
<TR>
<TD vAlign=top bgColor=#f0f2fb height=10></TD></TR>
<TR>
<TD vAlign=top bgColor=#f0f2fb>
<TABLE cellSpacing=0 cellPadding=0 width=590 align=center border=0>
<TBODY>
<TR>
<TD><SPAN class=f14> <STRONG>三.
关键实现<BR></STRONG><BR> 我们使用VC生成一个基于对话框的程序来演示卡拉OK字幕叠加的实现。程序界面如下:<BR><BR><IMG
height=174
onerror="this.src='http://www.yesky.com/image20010518/85985.jpg';"
hspace=3 src="VC实现卡拉OK字幕叠加_3.files/85985.jpg" width=513 align=center
vspace=1 border=1><BR>图4
演示程序界面<BR><BR> 为了使字幕叠加的过程更加清晰,我们设计了一个逻辑控制类CSubtitleController。在进行真正的字幕叠加之前,我们必须首先调用CSubtitleController类的SetTargetWindow函数设置字幕的显示窗口,随后调用SetSubtitleLine函数设置字幕行的内容、填充时间等属性。具体实现中,我们在主对话框类CKaraokeDemoDlg中定义一个CSubtitleController类的实例mController,并且在对话框的初始化函数OnInitDialog中进行了如下的调用:<BR><BR>BOOL
CKaraokeDemoDlg::OnInitDialog()
<BR>{<BR>CDialog::OnInitDialog();<BR><BR>// TODO: Add extra
initialization
here<BR>mController.SetTargetWindow(&mKaraokeWnd);<BR>mController.SetSubtitleLine(mSubtitleArray,
mDurationArray, 0, 5);<BR>// ......<BR><BR>return TRUE;
<BR>}<BR><BR> 其中,mKaraokeWnd表示字幕显示窗口,是一个CStatic类的对象实例;mSubtitleArray是CString类型的数组,用于存储字幕内容(注意,应将字幕行中的各个字符单独存储);mDurationArray是int类型的数组,用于存储字幕行中各个字符填充需要的时间。mSubtitleArray和mDurationArray可以在CKaraokeDemoDlg类的构造函数中做如下的初始化:<BR><BR>mSubtitleArray
= new CString[5];<BR>mDurationArray = new
int[5];<BR><BR>mSubtitleArray[0] = "真";<BR>mSubtitleArray[1] =
"的";<BR>mSubtitleArray[2] = "好";<BR>mSubtitleArray[3] =
"想";<BR>mSubtitleArray[4] = "你";<BR>mDurationArray[0] = 1500; //
以毫秒为单位<BR>mDurationArray[1] = 300;<BR>mDurationArray[2] =
1600;<BR>mDurationArray[3] = 500;<BR>mDurationArray[4] =
1000;<BR><BR> 主对话框类中还使用了一个定时器,定时间隔是40毫秒,即以每秒25帧的频率刷新字幕叠加的进度。我们在开始播放(即当用户按下“Play”按钮)时记下系统时间(存储到DWORD类型的变量mStartTime中),然后在每次定时到达的时候再次读取系统时间,与mStartTime做差值运算,得到当前播放到的时间点(我们暂且称之为流时间)。在定时器消息响应函数CKaraokeDemoDlg::OnTimer中,我们会调用CSubtitleController类的DrawSubtitle函数来完成实际的卡拉OK字幕输出,这个函数的参数就是这个流时间。<BR><BR> 在CSubtitleController类中,我们看到DrawSubtitle函数的具体实现如下:<BR><BR>BOOL
CSubtitleController::DrawSubtitle(DWORD
inStreamTime)<BR>{<BR>ASSERT(mClientDC);<BR><BR>DWORD timeInChar =
0; // 相对于当前字符填充的开始时间的时间<BR>LONG sungLength = 0; //
已经唱过的字幕宽度<BR><BR>// LocateChar为CSubtitleController类的一个私有函数<BR>//
根据当前播放到的时间点,定位到当前进度中的字符,<BR>// 并且得到播放时间点在当前字符中的相对时间<BR>int
currentChar = LocateChar(inStreamTime, timeInChar);<BR>if
(currentChar != -1) // 定位成功<BR>{<BR>// 计算已经唱过的字幕宽度<BR>//
mFromToArray数组记录各个字符的属性,包括开始、结束时间、尺寸等<BR>sungLength =
mFromToArray[currentChar].size.cx * timeInChar;<BR>sungLength =
sungLength / mFromToArray[currentChar].duration; <BR>for (int i = 0;
i < currentChar; i++)<BR>{<BR>//
累加上当前进度中的字符以前的所有字符的宽度<BR>sungLength +=
mFromToArray[i].size.cx;<BR>}<BR>}<BR>else<BR>{<BR>//
如果无法定位到任何一个字符,则画出整行<BR>sungLength = mTotalWidth;<BR>}<BR><BR>//
将字幕字体选入目标窗口的DC中<BR>CFont * pOldFont = (CFont *)
mClientDC->SelectObject(&mTextFont);<BR>mClientDC->SetBkMode(TRANSPARENT);
// 设置输出时背景透明<BR><BR>// 生成已经唱过的和尚未唱过的两块窗口区域<BR>//
mSungRegion和mSingingRegion均是CRgn类对象实例<BR>mSungRegion.CreateRectRgn(mStartPoint.x,
mStartPoint.y, <BR>mStartPoint.x + sungLength, mStartPoint.y +
mFromToArray[0].size.cy);<BR>mSingingRegion.CreateRectRgn(mStartPoint.x
+ sungLength, mStartPoint.y, <BR>mStartPoint.x + mTotalWidth,
mStartPoint.y + mFromToArray[0].size.cy);<BR><BR>//
画出第一部分:已经唱过的字幕(蓝色填充,白色勾边)<BR>int ret =
mClientDC->SelectClipRgn(&mSungRegion, RGN_COPY);
<BR>mClientDC->SetPolyFillMode(WINDING);<BR>HPEN pOldPen = (HPEN)
mClientDC->SelectObject(mSungBoundaryPen);<BR>HBRUSH pOldBrush =
(HBRUSH)
mClientDC->SelectObject(mSungTextBrush);<BR>mClientDC->BeginPath();
<BR>mClientDC->TextOut(mStartPoint.x, mStartPoint.y,
mSubtitleLine); <BR>mClientDC->EndPath();
<BR>mClientDC->StrokeAndFillPath(); //
画出字符路径并填充<BR>mClientDC->SelectClipPath(RGN_AND);<BR>//
恢复以前的画笔和画刷<BR>mClientDC->SelectObject(pOldPen);<BR>mClientDC->SelectObject(pOldBrush);<BR><BR>//
画出第二部分:尚未唱过的字幕(黑色勾边空心字)<BR>pOldPen = (HPEN)
mClientDC->SelectObject(mSingingBoundaryPen);<BR>pOldBrush =
(HBRUSH)
mClientDC->SelectObject(mSingingTextBrush);<BR>mClientDC->SelectClipRgn(&mSingingRegion,
RGN_COPY); <BR>mClientDC->BeginPath();
<BR>mClientDC->TextOut(mStartPoint.x, mStartPoint.y,
mSubtitleLine);
<BR>mClientDC->EndPath();<BR>mClientDC->StrokePath(); //
画出字符路径(不填充)<BR>mClientDC->SelectClipPath(RGN_AND);<BR>//
恢复以前的画笔和画刷<BR>mClientDC->SelectObject(pOldBrush);<BR>mClientDC->SelectObject(pOldPen);<BR>mSungRegion.DeleteObject();<BR>mSingingRegion.DeleteObject();<BR><BR>//
恢复目标窗口为“全区域”<BR>RECT
bounds;<BR>mTargetWnd->GetClientRect(&bounds);<BR>CRgn
rgn;<BR>rgn.CreateRectRgn(bounds.left, bounds.top, bounds.right,
bounds.bottom); <BR>ret = mClientDC->SelectClipRgn(&rgn,
RGN_COPY); <BR><BR>//
恢复以前的字体<BR>mClientDC->SelectObject(pOldFont);<BR><BR>//
如果无法定位到任何一个字符,则返回一个错误值<BR>return (currentChar != -1);<BR>}<BR><BR>//
根据当前播放到的时间点,定位到当前进度中的字符<BR>int CSubtitleController::LocateChar(DWORD
inStreamTime, DWORD & outTimeInChar)<BR>{<BR>//
mCharCount为整个字幕行的字符个数<BR>for (int i = 0; i < mCharCount;
i++)<BR>{<BR>if (inStreamTime >= mFromToArray[i].from &&
<BR>inStreamTime < mFromToArray[i].to)<BR>{<BR>outTimeInChar =
inStreamTime - mFromToArray[i].from;<BR>return
i;<BR>}<BR>}<BR>return
-1;<BR>}<BR><BR></SPAN><BR></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD vAlign=top bgColor=#f0f2fb height=10></TD></TR>
<TR>
<TD vAlign=top background=VC实现卡拉OK字幕叠加_3.files/newhard-040906_c_1.gif
bgColor=#f0f2fb height=1></TD></TR>
<TR>
<TD vAlign=top bgColor=#dee2f5>
<TABLE height=25 cellSpacing=0 cellPadding=0 width=590 align=center
border=0>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -