只提供对基本属性的读取(请参阅 MP3 的 ID3v1)。
/* 读取ASF(WMA、WMV)标签 */
public static MusicTagEntity ReadASFTag(String path)
{
try
{
char[] WMAHead = { 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C }; // WMA头部标识
char[] StandardHead = { 0x33, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C }; // 标准TAG头部标识
char[] ExtendHead = { 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 }; // 扩展TAG头部标识
String[] StandardHeadString = { "33", "26", "B2", "75", "8E", "66", "CF", "11", "A6", "D9", "00", "AA", "00", "62", "CE", "6C" }; // 标准TAG头部标识
String[] ExtendHeadString = { "40", "A4", "D0", "D2", "07", "E3", "D2", "11", "97", "F0", "00", "A0", "C9", "5E", "A8", "50" }; // 扩展TAG头部标识
MusicTagEntity mt = new MusicTagEntity();
File f = new File(path);
InputStream is = new BufferedInputStream(new FileInputStream(f));
BufferedReader br = new BufferedReader(new InputStreamReader(is, "ISO-8859-1")); // 头部需要用ASCII编码
// 读取头部判断是否为WMA文件
char[] buf = new char[16];
br.read(buf);
if (Arrays.equals(buf, WMAHead))
{// 是WMA
br.read(buf = new char[14]); // 跳过无用的8+6字节
br.read(buf = new char[16]); // 确定标签类型的头
// 需要判断这里是先扩展再标准还是先标准再扩展
if (Arrays.equals(buf, ExtendHead))
{// 扩展
br.read(buf = new char[8]); // 再次放过8字节(此处无用的8字节标志位)
br.read(buf = new char[2]); // 扩展标签的总个数
int ExtendCount = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
for (int i = 0; i < ExtendCount; i++)
{
br.read(buf = new char[2]); // 扩展名称长度
int FiledNameLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[FiledNameLength]); // 读取扩展名称
String strFieldName = Common.UnicodeCharToString(buf);
br.read(buf = new char[2]); // Flag,暂时不用
br.read(buf = new char[2]); // 扩展字段长度
int FiledLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[FiledLength]); // 读取扩展字段
if (strFieldName.equals("WM/TrackNumber"))
mt.setTrack(Common.UnicodeCharToString(buf));
if (strFieldName.equals("WM/Track"))
mt.setTrack(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/AlbumArtist"))
mt.setArtist(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/AlbumTitle"))
mt.setAlbum(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/Year"))
mt.setYear(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/Genre"))
mt.setGener(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/WM/GenreID"))
mt.setGener(Common.UnicodeCharToString(buf));
}
// 开始读取标准头
do
{// 跳过空白字符
br.read(buf = new char[1]);
}
while ((int) buf[0] == 0);
boolean IsStandartHeader = true; // 是否包含标准头部信息
if (Integer.toHexString((int) buf[0]).equals(StandardHeadString[0]))
{
for (int i = 1; i <= 15; i++)
{
br.read(buf = new char[1]);
String strHex = Integer.toHexString((int) buf[0]).toUpperCase();
if (strHex.length() == 1)
strHex = "0" + strHex;
if (!strHex.equals(StandardHeadString[i]))
{
IsStandartHeader = false;
break;
}
}
if (IsStandartHeader)
{// 找到标准头
br.read(buf = new char[8]); // 8字节无效内容
br.read(buf = new char[2]); // 标题长度
int TitleLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 艺术家长度
int ArtistLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 版权长度
int CopyrightLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 备注长度
int CommentLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 2字节无效内容
// 读取标题
br.read(buf = new char[TitleLength]);
mt.setTitle(Common.UnicodeCharToString(buf));
// 读取艺术家
br.read(buf = new char[ArtistLength]);
if (mt.getArtist().equals("")) // 如果扩展属性中没有此信息,则采用
mt.setArtist(Common.UnicodeCharToString(buf));
br.read(buf = new char[CopyrightLength]); // 跳过版权说明
// 读取备注
br.read(buf = new char[CommentLength]);
mt.setComment(Common.UnicodeCharToString(buf));
}
}
}
else if (Arrays.equals(buf, StandardHead))
{// 标准
br.read(buf = new char[8]); // 8字节无效内容
br.read(buf = new char[2]); // 标题长度
int TitleLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 艺术家长度
int ArtistLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 版权长度
int CopyrightLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 备注长度
int CommentLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[2]); // 2字节无效内容
// 读取标题
br.read(buf = new char[TitleLength]);
mt.setTitle(Common.UnicodeCharToString(buf));
// 读取艺术家
br.read(buf = new char[ArtistLength]);
mt.setArtist(Common.UnicodeCharToString(buf));
br.read(buf = new char[CopyrightLength]); // 跳过版权说明
// 读取备注
br.read(buf = new char[CommentLength]);
mt.setComment(Common.UnicodeCharToString(buf));
// 开始读取扩展头
do
{// 跳过空白字符
br.read(buf = new char[1]);
}
while ((int) buf[0] == 0);
boolean IsExtendHeader = true; // 是否包含标准头部信息
if (Integer.toHexString((int) buf[0]).equals(ExtendHeadString[0]))
{
for (int i = 1; i <= 15; i++)
{
br.read(buf = new char[1]);
String strHex = Integer.toHexString((int) buf[0]).toUpperCase();
if (strHex.length() == 1)
strHex = "0" + strHex;
if (!strHex.equals(ExtendHeadString[i]))
{
IsExtendHeader = false;
break;
}
}
if (IsExtendHeader)
{// 找到扩展头
br.read(buf = new char[8]); // 再次放过8字节(此处无用的8字节标志位)
br.read(buf = new char[2]); // 扩展标签的总个数
int ExtendCount = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
for (int i = 0; i < ExtendCount; i++)
{
br.read(buf = new char[2]); // 扩展名称长度
int FiledNameLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[FiledNameLength]); // 读取扩展名称
String strFieldName = Common.UnicodeCharToString(buf);
br.read(buf = new char[2]); // Flag,暂时不用
br.read(buf = new char[2]); // 扩展字段长度
int FiledLength = Integer.valueOf(Common.DecodeUnicodeHex(buf), 16); // 转换成数值
br.read(buf = new char[FiledLength]); // 读取扩展字段
if (strFieldName.equals("WM/TrackNumber"))
mt.setTrack(Common.UnicodeCharToString(buf));
if (strFieldName.equals("WM/Track"))
mt.setTrack(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/AlbumArtist"))
mt.setArtist(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/AlbumTitle"))
mt.setAlbum(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/Year"))
mt.setYear(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/Genre"))
mt.setGener(Common.UnicodeCharToString(buf));
else if (strFieldName.equals("WM/WM/GenreID"))
mt.setGener(Common.UnicodeCharToString(buf));
}
}
}
}
br.close();
is.close();
return mt;
}
else
{// 不是ASF格式
br.close();
is.close();
return null;
}
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
相关的字符编码转换方法:
/* 十六进制UnicodeChar转String */
public static String UnicodeCharToString(char[] Char)
{
String strTemp = "";
for (int i = 1; i < Char.length; i += 2)
{
String s1 = Integer.toHexString((int) Char[i]);
String s2 = Integer.toHexString((int) Char[i - 1]);
// 长度不足补全
if (s1.length() == 1)
s1 = "0" + s1;
if (s2.length() == 1)
s2 = "0" + s2;
strTemp += s1 + s2;
}
return UnicodeToString(strTemp).trim();
}
/* 将十六进制Unicode字符串转为汉字(将双字节转为单个字符) */
public static String UnicodeToString(String Hex)
{
String enUnicode = null;
String deUnicode = null;
for (int i = 0; i < Hex.length(); i++)
{
if (enUnicode == null)
enUnicode = String.valueOf(Hex.charAt(i));
else
enUnicode = enUnicode + Hex.charAt(i);
if (i % 4 == 3)
{
if (enUnicode != null)
{
if (deUnicode == null)
deUnicode = String.valueOf((char) Integer.valueOf(enUnicode, 16).intValue());
else
deUnicode = deUnicode + String.valueOf((char) Integer.valueOf(enUnicode, 16).intValue());
}
enUnicode = null;
}
}
return deUnicode;
}
/* 倒序排列十六进制Unicode(用以计算该字节标识的数值) */
public static String DecodeUnicodeHex(char[] Char)
{
String strTemp = "";
boolean CouldContinue = true;
for (int i = Char.length - 1; i >= 0; i--)
{
String strHex = Integer.toHexString((int) Char[i]);
if (strHex.length() == 1)
strHex = "0" + strHex;
if (strHex.equals("00") && CouldContinue)
continue;
else
{
strTemp += strHex;
CouldContinue = false;
}
}
return strTemp;
}
此外还需要一个用于接收信息的类(MusicTagEntity.java):
package *; //请自行填写
public class MusicTagEntity
{
private String Album = ""; // 专辑名称
private String Title = ""; // 歌曲标题
private String Gener = ""; // 流派
private String Artist = ""; // 艺术家
private String Year = ""; // 年份
private String Track = ""; // 音轨号
private String Lyric = ""; // 歌词
private String Comment = ""; // 备注
public String getAlbum()
{
return Album;
}
public void setAlbum(String album)
{
Album = album;
}
public String getTitle()
{
return Title;
}
public void setTitle(String title)
{
Title = title;
}
public String getGener()
{
return Gener;
}
public void setGener(String gener)
{
Gener = gener;
}
public String getArtist()
{
return Artist;
}
public void setArtist(String artist)
{
Artist = artist;
}
public String getYear()
{
return Year;
}
public void setYear(String year)
{
Year = year;
}
public String getTrack()
{
return Track;
}
public void setTrack(String track)
{
Track = track;
}
public String getLyric()
{
return Lyric;
}
public void setLyric(String lyric)
{
Lyric = lyric;
}
public String getComment()
{
return Comment;
}
public void setComment(String comment)
{
Comment = comment;
}
}
apple 自带字典的解释:
cocoa |ˈkōkō|
noun
1 a chocolate powder made from roasted and ground cacao seeds.
• a hot drink made from such a powder mixed with sugar and milk or water.
2 variant spelling of coco , usu. regarded as a misspelling.
ORIGIN early 18th cent. (denoting cacao seed): alteration of cacao .
http://www.cocoachina.com/cms/uploads/100324/cocoa.mp3
转自 http://www.mcu123.com/news/Article/all/Analog/200608/171.html
34063由于价格便宜,开关峰值电流达1.5A,电路简单且效率满足一般要求,所以得到广泛使用。在ADSL应用中,34063的开关频率对传输速率有很大影响,在器件选择及PCB设计时需要仔细考虑。
线性稳压电源效率低,所以通常不适合于大电流或输入、输出电压相差大的情况。开关电源的效率相对较高,而且效率不随输入电压的升高而降低,电源通常不需要大散热器,体积较小,因此在很多应用场合成为必然之选。开关电源按转换方式可分为斩波型、变换器型和电荷泵式,按开关方式可分为软开关和硬开关。
斩波型开关电源
斩波型开关电源按其拓扑结构通常可以分为3种:降压型(Buck)、升压型(Boost)、升降压型(Buck-boost)。降压型开关电源电路通常如图1所示。
图1中,T为开关管,L1为储能电感,C1为滤波电容,D1为续流二极管。当开关管导通时,电感被充磁,电感中的电流线性增加,电能转换为磁能存储在电感中。设电感的初始电流为iL0,则流过电感的电流与时间t的关系为:
iLt= iL1+(Vi-Vo-Vs)t/L,Vs为T的导通电压。
当T关断时,L1通过D1续流,从而电感的电流线性减小,设电感的初始电流为iL1,则则流过电感的电流与时间t的关系:
iLt=iL1-(Vo+Vf)t/L,Vf为D1的正向饱和电压。
图1 降压型开关电源基本电路
34063的特殊应用
● 扩展输出电流的应用
DC/DC转换器34063开关管允许的峰值电流为1.5A,超过这个值可能会造成34063永久损坏。由于通过开关管的电流为梯形波,所以输出的平均电流和峰值电流间存在一个差值。如果使用较大的电感,这个差值就会比较小,这样输出的平均电流就可以做得比较大。例如,输入电压为9V,输出电压为3.3V,采用220μH的电感,输出平均电流达到900mA,峰值电流为1200mA。
单纯依赖34063内部的开关管实现比900mA更高的输出电流不是不可以做到,但可靠性会受影响。要想达到更大的输出电流,必须借助外加开关管。图2和图3是外接开关管降压电路和升压电路。
图2 升压型达林顿及非达林顿接法
图3 降压型达林顿及非达林顿接法
采用非达林顿接法,外接三极管可以达到饱和,当达到深度饱和时,由于基区存储了相当的电荷,所以三极管关断的延时就比较长,这就延长了开关导通时间,影响开关频率。达林顿接法虽然不会饱和,但开关导通时压降较大,所以效率也会降低。可以采用抗饱和驱动技术,图4所示,此驱动电路可以将Q1的Vce保持在 0.7V以上,使其导通在弱饱和状态。
图4 抗饱和驱动电路
利用一片34063就可以产生三路电压输出,如图5所示。
图5 输出3路电压的34063电路
+VO的输出电压峰值可达2倍V_IN,-VO的输出电压可达-V_IN。需要注意的是,3路的峰值电路不能超过1.5A,同时两路附加电源的输出功率和必须小于V_IN·I·(1-D),其中I为主输出的电流,D为占空比。在此两路输出电流不大的情况下,此电路可以很好地降低实现升压和负压电源的成本。
● 具有关断功能的34063电路
34063本身不具有关断功能,但可以利用它的过流饱和功能,增加几个器件就可以实现关断功能,同时还可以实现延时启动。
图6是具有关断功能的34063电路,R4取510Ω,R6取3.9kΩ。当控制端加一个高电平,则34063的输出就变成0V,同时不影响它的过流保护功能的正常工作。
将此电路稍加改动,就可以得到具有延时启动功能的34063电路,如图7所示。
取C11为1μF,R10为510Ω,就可以达到200~500ms的启动延时(延时时间和输入电压有关)。这个电路的缺点就是当峰值电流过流时无法起到保护作用,只能对平均电流过流起保护作用。
● 恒流恒压充电电路
恒压恒流充电电路如图8所示,可用于给蓄电池进行充电,先以500mA电流恒流充电,充到13.8V后变为恒压充电,充电电流逐渐减小。
34063的局限性
由34063构成的开关电源虽然价格便宜、应用广泛,但它的局限性也是显而易见的。主要有以下几点:
(1)效率偏低。对于降压应用,效率一般只有70%左右,输出电压低时效率更低。这就使它不能用在某些对功耗要求严格的场合,比如USB提供电源的应用。
(2)占空比范围偏小,约在15%~80%,这就限制了它的动态范围,某些输入电压变化较大的应用场合则不适用。
(3)由于采用开环误差放大,所以占空比不能锁定,这给电路参数的选择带来麻烦,电感量和电容量不得不数倍于理论计算值,才能达到预期的效果。虽然34063有许多缺点,但对产品利润空间十分有限的制造商来说,它还是设计开关电源的很好选择。
图6 具有关断功能的34063电路
图7 具有延时启动功能的34063电路
图8 恒压恒流充电电路
开关电源的频率和ADSL性能
对于ADSL来说,上行信道分布在30~100kHz之间,下行信道分布在100kHz~1.1MHz之间。长线连接速率常常是衡量ADSL性能的一个重要指标,但在线路很长的时候,下行信道中高频信道衰减得很厉害,所以此时下行低频段的信噪比对长线连接速率就起着至关重要的作用。
开关电源的输出含有开关频率基频及其谐波的纹波成分,一般从基波到10次谐波的能量都比较大。如果开关频率为20kHz,它的谐波为40kHz、60kHz、80kHz…。这样,从100~300kHz的下行信道中就会有10个干扰的频率点。而如果开关频率为100kHz,则干扰点就下降为2个,如果开关频率为1MHz,则下行信道就不会受到干扰,这样就能极大提高下行信道的性能。
器件选择要点
(1)只如果外接开关管,最好选择开关三极管或功率MOS 管,注意耐压和功耗。
(2)如果开关频率很高,电感可选用多线并绕的,以降低趋肤效应的影响。
(3)续流二极管一般选恢复时间短、正向导通电压小的肖特基二极管,但要注意耐压。如果输出电压很小(零点几伏),就必须使用MOS管续流。输出滤波电容一般使用高频电容,可减小输出纹波同时降低电容的温升。在取样电路的上臂电阻并一个0.1~1μf电容,可以改善瞬态响应。
PCB布局和布线的要点
开关导通和关断都存在一个电流环路,这两个环路都是高频、大电流的环路,所以在布局和布线时都要将此二环路面积设计得最小。用于反馈的取样电压要从输出电容上引出,并注意芯片或开关管的散热。
参考文献
1 张占松,蔡宣三.开关电源的原理与设计.电子工业出版社,1998
2 Motorola Inc..MC34063A DC-DC Converter Control Circiuts. Motorola Inc.,1996