在 C# 中通过 ffmpeg.exe 获取媒体文件的音频波形数据
以前这个功能我是通过 NAudio 实现的,需要把媒体文件的音频轨提取成单独的 WAV 文件才能获取到波形数据。即使借助 RAM Disk,速度也依然很慢。

忍受了一段时间后,昨天又搜了一遍 C# 下可用的音频处理类库,依然没有更好的选择。最后想到在视频转码和截图时常用的 mencoder, ffmpeg 等跨平台的命令行工具了。看了一下 ffmpeg 支持 stdout 输出,貌似可用。

最开始尝试了输出 WAV 格式的二进制流到 stdout,结果发现输出格式为 WAV 时,ffmpeg 会先在头部写一个占位的大小信息,在整个流输出结束后,再 seek 到那个位置改写大小信息 (来源)。这样当输出不是直接写入到文件,而是以 stdout 流直接输出时,这个改写操作就不能进行了,最后得到的 WAV 格式的数据流中的信息有错误,也就无法使用了。

使用 WAV 格式只是因为对它比较熟悉,毕竟是常见的最基本的直接存储型的音频文件格式了。但在翻阅 ffmpeg 文档后,发现它支持的格式非常多和灵活,可以选择直接输出整型或浮点数的波形数据,而不带额外的头信息。这正方便我用 C# 处理,最终我选择了最适用于后续处理的 Little Endian 32 位浮点数格式,也就是调用 ffmpeg.exe 时给定 "-f f32le" 参数。"-ar 44100 -ac 1" 参数则使输出固定为单声道 44100Hz 采样率的数据。

最后,完整的代码如下。Main() 方法中调用 ffmpeg.exe 将 test.mp4 的音频轨以单声道,44100Hz 采样率,32 位浮点数的形式输出到 stdout,由 ProcessStream() 对流 proc.StandardOutput.BaseStream 进行读取,最终由 ProcessBuffer() 方法获取每一个单精度浮点数。

  1. using System.Diagnostics;
  2. using System.IO;
  3.  
  4. // ...
  5.  
  6. static void Main(string[] args)
  7. {
  8.     // ...
  9.     string path = @"E:\Media\test.mp4";
  10.  
  11.     Process proc = new Process();
  12.     proc.StartInfo.FileName = @"E:\ffmpeg\ffmpeg.exe";
  13.     proc.StartInfo.Arguments = "-i \"" + path + "\" -vn -ar 44100 -ac 1 -f f32le -";
  14.     proc.StartInfo.CreateNoWindow = true;
  15.     proc.StartInfo.UseShellExecute = false;
  16.     proc.StartInfo.RedirectStandardOutput = true;
  17.     proc.StartInfo.RedirectStandardError = true;
  18.     proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
  19.     proc.Start();
  20.     proc.BeginErrorReadLine();
  21.     ProcessStream(proc.StandardOutput.BaseStream);
  22.  
  23.     proc.WaitForExit(10000); // 10s
  24.     if (!proc.HasExited)
  25.     {
  26.         proc.Kill();
  27.         Environment.Exit(1);
  28.     }
  29.     // ...
  30. }
  31.  
  32. static void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
  33. {
  34.     if (e.Data != null)
  35.     {
  36.         // Console.WriteLine(e.Data);
  37.         // do nothing
  38.     }
  39. }
  40.  
  41. static void ProcessStream(Stream stream)
  42. {
  43.     int didread;
  44.     int offset = 0;
  45.     byte[] buffer = new byte[sizeof(Single) * (1024 + 1)];
  46.  
  47.     int length, residual_length;
  48.  
  49.     while ((didread = stream.Read(buffer, offset, sizeof(Single) * 1024)) != 0)
  50.     {
  51.         length = offset + didread;
  52.         residual_length = length % sizeof(Single);
  53.  
  54.         if (residual_length == 0) {
  55.             ProcessBuffer(buffer, length);
  56.             offset = 0;
  57.         } else {
  58.             length -= residual_length;
  59.             ProcessBuffer(buffer, length);
  60.             Array.Copy(buffer, length, buffer, 0, residual_length);
  61.             offset = residual_length;
  62.         }
  63.     }
  64. }
  65.  
  66. static void ProcessBuffer(byte[] buffer, int length)
  67. {
  68.     int index = 0;
  69.     float sample_value;
  70.  
  71.     while (index < length)
  72.     {
  73.         sample_value = BitConverter.ToSingle(buffer, index);
  74.         index += sizeof(Single);
  75.         // to deal with sample_value
  76.     }
  77. }
  78.  
  79. // ...
当前语言: 中文 (简体) · also available in: English
音乐节拍跟踪演示视频
这些天写了一个基于 FPGA 和 ARM 的音乐节拍实时跟踪算法,FPGA 的 Verilog 代码写得差不多了,但后续 ARM 部分的处理就比较难搞一些。

算法的原型是用 MATLAB 的 M 代码写的,下面这个视频就是用的 MATLAB 里输出的结果进行的灯光效果展示。注意看的时候要选高清以上的清晰度,否则由于压缩的关系画面明暗会发生跳变。



目前来看这个算法的运算量和内存空间占用都太大了,也没能很好的达到预期效果,以后还得尝试换运算量小的算法。

下面这个是更早几天的视频,是上面视频中最后一首 True Romance 的节拍跟踪效果的预览视频。最开始打算写这个节拍跟踪算法就是因为这首歌,想把鼓和钢琴的节拍能同时展示出来。最终效果还是不令人满意。

当前语言: 中文 (简体) · also available in: English
《将 Windows 7 配置成 Windows XP 的样子》补图
将 Windows 7 配置成 Windows XP 的样子》一文写完后本应配上成果截图,但因懒得整理文件一直没做此项工作。现补上几张简单的截图:


桌面


开始菜单和任务栏


资源管理器


还是资源管理器


控制面板


Firefox
当前语言: 中文 (简体) · also available in: English
我的 DIY 常用器件列表
先将主要的 IC 部分整理出来了,剩余的还有二极管、BJT、MOS、开关、电位器和传感器。这些器件都是个人 DIY 时容易从淘宝上少量购买的,价格便宜且性能能满足一般要求。

点击元件型号可以直接在淘宝中搜索,默认按销量从高到低排序。点击型号右侧的 PDF 图标可以通过 Google 搜索 datasheet,用搜索引擎容易找到官方网站提供的最新的 datasheet。

IC - 电源管理

DC-DC
FP6291 1MHz 2.5A 升压电流模式 PWM 转换器 SOT23-6
AP2953A 3A 18V 同步整流降压转换器 SOP-8
MP2359 1.2A 24V 1.4MHz 降压转换器 SOT23-6
PT1301 0.8V 低启动电压升压 DC-DC 转换器 SOT-23-6, SOT-89-5
XR2203 1.2MHz 26V 升压 DC-DC 转换器 SOT23-5
LDO
AMS1117 1.3V(max) @ 800mA 压降, 800mA LDO SOT-223, TO-252
HT73XX 0.1V(typ) @ 40mA 压降, 静态电流 3.5uA, 低功耗 LDO SOT-89, TO-92
HT75XX 0.1V(typ) @ 1mA 压降, 最高 24V 输入电压 LDO SOT-89, TO-92
XC6206 0.7V(max) @ 100mA 压降, 200mA 小体积 LDO SOT-23, SOT-89, TO-92
MIC29302 0.6V(max) @ 3A 压降, 3A 大电流 LDO TO-220-5, TO-263-5
MIC5209 0.5V(max) @ 500mA 压降, 500mA LDO SOT-223, SOP-8, TO-263-5
隔离 DC-DC
B0505S-1W 5V 输入, 5V 输出, 1W DC-DC 隔离电源 SIP, DIP
B0505S-2W 5V 输入, 5V 输出, 2W DC-DC 隔离电源 SIP
VREF
TL431A 2.5V - 36V 电压基准, 30ppm/℃, 电流 1mA - 100mA SOT-23, TO-92
LM385-1.2 1.235V 电压基准, 30ppm/℃, 工作电流 10uA - 20mA SOT-23, TO-92, SOIC-8
LM385-2.5 2.5V 电压基准, 30ppm/℃, 工作电流 20uA - 20mA SOT-23, TO-92, SOIC-8
MC1403 2.5V 电压基准, 10ppm/℃, 输入电压 4.5V - 40V DIP-8, SOIC-8
充电
CN3058 500mA 磷酸铁锂电池充电器 SOP-8
TP4056 1A 线性锂离子电池充电器 SOP-8, MSOP-8
TP4057 500mA 线性锂离子电池充电器 SOT-23-6

IC - 线性

运放
LM358 0.7MHz 双运放, Vcc = 3V - 32V SOP-8, DIP-8
LM324 1.2MHz 四运放, Vcc = 3V - 32V SOP-14, DIP-14
MCP6002 1MHz RRIO CMOS 双运放, I = 100uA, Ib = 1pA(max) SOP-8, DIP-8
AD8605 10MHz 高精度低噪声 RRIO CMOS 运放, Vos = 65uA(max) SOT-23-5
AD8628 2.5MHz RRIO 斩波放大器, Vos = 1uV(typ) SOT-23-5
MAX4239 6.5MHz 斩波放大器, Vos = 0.1uV(typ) SOT-23-6, SOP-8
比较器
LM393 双比较器, Vcc = 2V - 36V SOP-8, DIP-8
LM339 四比较器, Vcc = 2V - 36V SOP-14, DIP-14
LM311 双高速比较器, Vcc = 3.5V - 30V SOP-8, DIP-8
麦克风放大
MAX9812LEXT 20dB 固定增益单输入麦克风放大器 SC-70-6
MAX9813LEKA 20dB 固定增益双输入麦克风放大器 SOT-23-8

IC - 数据采集

ADC
CS5343 24bit, 96kHz 音频 ADC, I2S 接口 TSSOP-10
TM7705 16bit Σ-Δ ADC SOP-16, DIP-16
CS5550 应用于电子秤的两通道 24bit Σ-Δ ADC SSOP-24
DAC
CS4344 24bit, 192kHz 音频 DAC, I2S 接口 TSSOP-10
TM8211 16bit 音频 DAC SOP-8

IC - 逻辑

逻辑门
74HC00 四 2 输入与非门 SOP-14, DIP-14
74HC04 六反相器 SOP-14, DIP-14
74HC14 六反相施密特触发器 SOP-14, DIP-14
74HC74 双 D 触发器 SOP-14, DIP-14
74HC138 3-8 译码器 SOP-16, DIP-16
74HC595 8 位串入/串出或并出移位寄存器 SOP-16, DIP-16
74HC245 8 位总线收发器 SOP-20, DIP-20
74LVC245 8 位总线收发器, 输入输出引脚可耐压 5V SOP-20
74LVC4245 8 位总线收发器, 输入输出引脚可耐压 5V, 双路电源 SOP-24, TSSOP-24

IC - 接口

收发器
MAX3232 3V - 5.5V RS-232 收发器 SOP-16, DIP-16
PL2303HX USB to RS-232 转换器 SSOP-28
模拟开关
74HC4051 8 通道多路复用器, Vcc = 2V - 10V SOP-16, DIP-16
74HC4052 双 4 通道多路复用器, Vcc = 2V - 10V SOP-16, DIP-16
74HC4053 三 2 通道多路复用器, Vcc = 2V - 10V SOP-16, DIP-16

IC - MCU

Cortex-M3
STM32F100C8T6B 中容量 24MHz MCU, 1×12bit ADC, 2×12bit DAC, 64KB Flash, 8KB SRAM LQFP-48
STM32F103C8T6 中容量 72MHz MCU, 2×12bit ADC, USB 2.0 FS, 64KB Flash, 20KB SRAM LQFP-48
STM32F103VBT6 中容量, 独立 VREF+ 引脚 72MHz MCU, 2×12bit ADC, 128KB Flash, 20KB SRAM LQFP-100
STM32F103VCT6 大容量, 独立 VREF+ 引脚 72MHz MCU, 3×12bit ADC, 2×12-bit DAC, I2S, SDIO, 256KB Flash, 48KB SRAM LQFP-100
MSP430
MSP430F149 8MHz MCU, 12bit 200ksps SAR ADC, 48 GPIOs, 60KB Flash, 2KB SRAM LQFP-64, TQFP-64
MSP430F2012IPW 16MHz MCU, 10bit 200ksps SAR ADC, 10 GPIOs, 2KB Flash, 128B SRAM TSSOP-14, DIP-14

IC - 存储器

SDRAM
H57V2562GTR-75C 256M (16Mx16bit) SDRAM, 133MHz TSOP-54
当前语言: 中文 (简体)
入手赛克 858D 热风拆焊台
买不买热风枪这个问题纠结了我两年多,因为这玩意买来后用的机会实在是太少了。业余做点东西不会选 BGA 封装的芯片,因此热风枪的用途就只剩拆元件了,还得是拆烙铁不好搞定的元件。

不过最近遇到几次需要拆 USB 插座和 LQFP 封装芯片的情况,没热风枪还真是很难弄,用烙铁拆很容易拆坏。因此决定买一个热风枪了,但毕竟用的机会还是很少,所以体积绝对不能大,这样不用的时候收到哪也省地。

在淘宝上搜了一圈,发现主要有四种热风枪类型。第一种是下面这种长的像电吹风的,看着就不像拆元件用的。



第二种是这种很便宜的 8032 便携式热风枪,电机、发热芯、调节旋钮都在手柄上,光看外观就有一股浓浓的山寨气息。应该不好用,也不耐用。



第三种是有气泵的 850 气泵式热风拆焊台,这种很专业了,很多人推荐。但是这种有气泵的拆焊台的最大问题就是体积太大了,顶部还有一个提手,想在上边摞一个 936 焊台都不行。非数显的型号是 850 或 850A,数显的型号是 850+ 或 850D 等。



第四种就是我最终选择的 858 无刷风机拆焊台,风扇和发热芯在手柄中,其余控制及电源电路在机箱中。非数显的型号是 858,数显的型号是 858D。



下面这个图片中标明了尺寸,13.0cm(H)*10.0cm(W)*14.7cm(L) 的体积和前面 850 的 13.5cm(H)*18.7cm(W)*24.5cm(L) 相比小了很多。可以直接摞在 936 焊台上,很节省空间。



下面是 858 热风台和 936 焊台摆在一块时的尺寸对比。



在确定 858D 这个型号后,还要考虑买哪个牌子的。在淘宝上搜“858D”按销量排序,排在前面的有谊华、安赛、安泰信、森沃、赛克和快克这几个牌子,快克的贵出一截直接排除了,听着耳生、价格也相对低的那几个牌子看评论有反映各种小问题的。最后在安泰信和赛克中选择了赛克,安泰信的评论中有点人反映加热时味道比较大,赛克的看着是没什么问题,另外比安泰信的还稍便宜点。

EEVBlog 的 David 评测过安泰信的 858D+ 热风台,他是在 2011 年时从中国买的,机器加运费一共 82 澳元,按当时的汇率合 500 多元人民币了。大陆当时淘宝上估计最多也就卖 200 吧,咱们这买这些设备价格还是很好的。
当前语言: 中文 (简体)
淘宝批量好评书签小工具
淘宝以前评论时一直有批量选择好评的复选框,但是大概从一年前开始就消失了,每次评论都得一个一个点好评。其实写个脚本也不麻烦,但一直懒得写。最近有个单子商品数量实在是太多了,在向淘宝客服确认现在确实没这个功能后,写了下面这段代码:

  1. function(){
  2.     var objs = document.getElementsByTagName('input');
  3.     for (var i = 0; i < objs.length; i++) {
  4.         var obj = objs[i];
  5.         if (typeof(obj.type) != 'undefined' && obj.type == 'radio') {
  6.             if (obj.className == 'good-rate') {
  7.                 obj.checked = true;
  8.             } else if (obj.name == 'description' || obj.name == 'attitude'
  9.                        || obj.name == 'delivery' || obj.name == 'logistics') {
  10.                 if (obj.value == '5') {
  11.                     obj.click();
  12.                 }
  13.             }
  14.         }
  15.     }
  16. }

把下面这个链接拖拽到标签栏,需要全部选好评时点一下就行了:

淘宝批量好评

如果想只批量评价商品,而不评论店铺的话,使用下面这个版本:

  1. function(){
  2.     var objs = document.getElementsByTagName('input');
  3.     for (var i = 0; i < objs.length; i++) {
  4.         if (typeof(objs[i].type) != 'undefined' && objs[i].type == 'radio' && objs[i].className == 'good-rate') {
  5.             objs[i].checked = true;
  6.         }
  7.     }
  8. }

要拖拽的链接是下面这个:

商品批量好评

我自己有一个本地的淘宝收藏夹程序,程序完成时同时写了 Greasemonkey 脚本和做了 Bookmarklet,后来淘宝的页面经过一次改动,Greasemonkey 脚本失效了,而 Bookmarklet 简单可靠,一直能用,因此这次只写一个 Bookmarklet。
当前语言: 中文 (简体)
更多条目: [1] [2] [3] [4] [5] [6] [7] [8] [9] ... [22]
« 上一页 · 下一页 »