Long Luo's Life Notes

每一天都是奇迹

By Long Luo

一、在线搜索

之前搜索页面的一些缺陷:

  1. 具体实现位于 VideoListActivity 中,一方面会造成 VideoListActivity 代码过于庞大臃肿,另外一方面不便于后续功能扩展,结构不清晰;
  2. 依赖了大量系统控件,不便于后续解耦界面定制
  3. 今后搜索界面会参考第三方视频应用的实现,之前不便于增加搜索记录,或搜索独立出来,用于搜索本地视频,甚至将此搜索移植应用于其他应用中;

1.1 在线搜索实现效果

在线搜索因为是和第三方合作,涉及到很多网络相关的操作,简单来说就是利用 Http 协议向相关接口发起一次网络请求,服务器如果返回了正确的响应,App 会解析服务器返回的内容,并展示出来。

1.1.1 热词界面

热词界面是当搜索文本框文字为空时会弹出热词界面,会展示最近一段时间内搜索频率很高的词语。一方面可以节省大家输入文字,另外一方面你也可以了解当前的一些热点。

当你点击列表中的某个热词时,就会发起一次以此为关键词的搜索。

热词显示

1.1.2 关联词

搜索文本输入框含有文字时,会获取当前输入文字,以此为关键词获取网络的一些联想词,可以点击此联想词发起一次搜索。

关联词显示

1.1.3 搜索结果分类浏览

发起一次搜索之后,如果得到了服务器的正确响应,而且确实有相关视频内容。那么我们会将搜索结果展示在手机页面上。

搜索到的结果可以分不同频道浏览,会根据具体内容进行动态变化,有的可能有十几个频道,有的也就一个频道。频道页面可以滑动浏览,也可以选择在顶部页面选中或者滑动。

分类浏览时,第一个展示的页面是搜索到的全部视频内容,之后的会根据结果动态变化。

如下图所示:

搜索结果

分频道浏览:

搜索结果分类

1.1.4 语音搜索

语音搜索图标只有当搜索框里文字为空才会出现,否则出现搜索图标

点击语音搜索图标将会启动 VoiceSearch 这个 apk ,然后你可以说话,如果被正确识别之后,会发起一次搜索,并将结果展示出来。

语音搜索

1.1.5 语音搜索结果

语音搜索结果
阅读全文 »

翻译 By Long Luo

原文链接:Android Audio: Play a WAV file on an AudioTrack

译者注: 1. 由于这是技术文章,所以有些词句使用原文,表达更准确。 2. 由于水平有效,有些地方可能翻译的不够准确,如有不当之处,敬请批评指正. 3. 针对某些语句,适当补充了上下文及更适合中文阅读,尽量做到信达雅。


如果你已经成功地了解了关于AudioTrack一些话题,那么你可能享受它带来的好处,例如低延迟(在STATIC(静态)模式),能够生成流式音频(在STREAM(流)模式)以及在播放之前,就能够访问和修改原始声音数据。

不过,现在的问题是如何从源获取数据。许多应用需要使用的AudioTrack并不能简单的生成PCM音频(一个例子,比如Ethereal Dialpad或者其他类似的App)。你可能需要从文件源去加载数据,例如WAVMP3文件。

不要期望使用MediaPlayer,去解码WAV文件和MP3音频。虽然MediaPlayer播放这些文件非常好,但是其播放逻辑完全在Native层,同时并没有为我们提供额外选项,允许我们使用其他解码器实现我们的目的。因此,我们必须从手动地从音频文件进行解码出PCM

在这篇文章中,将会讨论WAV格式文件。而在下一课中,我们将会更进一步,讨论如何从MP3文件读取音频。

背景知识: 一些数字音频术语

如果你的App不是专门为数字音频设计,那么在继续我们的讨论之前,你可能需要先了解一些基本的缩略语。别担心,都很简单,我们不需要对此做深入挖掘。

  • PCM(脉冲调制方式) - 实现一个物理音频信号变成数字化最简单方法。基本原理就是信号变成了一个数字阵列,而其中每个数字代表的是声音在特定的时间瞬间的电平也可以说是能量(振幅)。(如果这种解释在科学上可能不会很准确,那我就只能说声抱歉了)。信不信由你,你可以使用这种方法表示任何复杂的声音,而且回放出来也非常精准。在这里,我们将只会谈到线性PCM。在线性PCM中,其中阵列中的每个数字都是原始声音振幅的线性表示。在某些情况下,对数映射能够更好地表示原来的声音幅度比例情况 - 但是我们不会讨论那些情况。

  • Sampling rate(采样率):- 每秒你的数字声音有多少样本(声音幅度用数字表示)。样本越多,你能得到声音质量越好。目前在消费类音频系统目前使用的采样率通常是22050,44100和48000Hz/s。

  • 每个样品分辨率/采样大小/位 - 定义表示振幅数字的大小和格式。例如,如果您使用的是8位整数,你只能表达出256级的幅度,所以原来的物理波形将被简化为256个离散电平,与此同时,你将失去一些声音精度也可以说是质量。如果你使用16位,那么声音质量变得更好。事实上,大部分时间你可能会使用16位音频。其他选项包括24位,32位(这些都是Android现在不支持的),或是使用浮点数。

  • 声道 - 既可以是单声道,也可以是立体声(2个声道),或者更多声道(但是Android不支持)。如果你想要有立体声,你需要有立体声音频,就必须要在每个声道都需要有一个独立的PCM数组,相应的信息量也会翻倍。

上述定义也有助于你理解特定的格式和长度的音频缓冲区的数据量,以便提前预备缓冲区。也就是你需要一个缓冲区,以用于存储5秒长度以44100Hz采样率的立体声16-bit线性PCM数据。数据计算公式如下所示:

5 sec * 44100 samples per sec * 2 bytes per sample * 2 channels = 882,000 bytes

这一数额所需的内存可能会让初学者感到惊讶,因为当你往你的磁盘上存储的音频时,一个MP3文件,一个880KB的文件就可以容纳以相同的采样率和分辨率1分钟时长的音轨。这是为什么呢?因为先进的格式,比如MP3格式。因为我们大脑无法分辨识别出一些音频的内容,所以使用了很多复杂的方式在压缩的过程中去掉了这些内容。然而,大多数低等级的音频API,包括Android的AudioTrack只能接受线性PCM。这就是为什么如果我们不能把整个样品都放在内存中,我们需要将要处理的数据流,循环缓冲区和其他聪明的方式来使用音频API。

希望这样的解释并没有让你产生困惑,现在让我们继续来实际做一些与Android上的数字音频有关的工作吧!

WAV文件格式

我们的目标是用一个InputStream,由其从一个WAV文件加载PCM数据,来提供原始字节数据。然后我们就可以将原始的PCM数据直接推送到使用已经正确的配置好了的AudioTrack.write,通过使用AudioTrack.write()这个API。

WAV文件包含一个文件头和具体数据会。我们需要读取文件头以知道诸如采样速率,分辨率等信息。另外,我们通过文件头,也可以知道此格式是否支持。WAV可以封装成多种格式,我们无法全部支持。也许,只是合理的采样率,分辨率和通道的线性PCM格式。

WAV格式的细节在互联网上都可以找到,你仅仅需要在Google上搜索下。但是,遗憾的是,我并没有搜索到一个很好的Java库来读取WAV文件,而且可以移植到Android下。因此,我自己写了一些简单的代码。

下面这个方法就是如何读取一个WAV文件的头部:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
private static final String RIFF_HEADER = "RIFF";
private static final String WAVE_HEADER = "WAVE";
private static final String FMT_HEADER = "fmt ";
private static final String DATA_HEADER = "data";

private static final int HEADER_SIZE = 44;

private static final String CHARSET = "ASCII";

/* ... */

public static WavInfo readHeader(InputStream wavStream) throws IOException,
DecoderException {

ByteBuffer buffer = ByteBuffer.allocate(HEADER_SIZE);
buffer.order(ByteOrder.LITTLE_ENDIAN);

wavStream.read(buffer.array(), buffer.arrayOffset(), buffer.capacity());

buffer.rewind();
buffer.position(buffer.position() + 20);
int format = buffer.getShort();
checkFormat(format == 1, "Unsupported encoding: " + format); // 1 means
// Linear
// PCM
int channels = buffer.getShort();
checkFormat(channels == 1 || channels == 2, "Unsupported channels: "
+ channels);
int rate = buffer.getInt();
checkFormat(rate <= 48000 && rate >= 11025, "Unsupported rate: " + rate);
buffer.position(buffer.position() + 6);
int bits = buffer.getShort();
checkFormat(bits == 16, "Unsupported bits: " + bits);
int dataSize = 0;
while (buffer.getInt() != 0x61746164) { // "data" marker
Log.d(TAG, "Skipping non-data chunk");
int size = buffer.getInt();
wavStream.skip(size);

buffer.rewind();
wavStream.read(buffer.array(), buffer.arrayOffset(), 8);
buffer.rewind();
}
dataSize = buffer.getInt();
checkFormat(dataSize > 0, "wrong datasize: " + dataSize);

return new WavInfo(new FormatSpec(rate, channels == 2), dataSize);
}

上面的代码中,缺少的部分应该是显而易见的。正如你所看到的,仅仅支持16位,但在你可以修改代码以支持8位(AudioTrack不支持任何其他分辨率的)。

下面这个方法,则是用来读取文件剩余的部分 - 音频数据

1
2
3
4
5
6
public static byte[] readWavPcm(WavInfo info, InputStream stream)
throws IOException {
byte[] data = new byte[info.getDataSize()];
stream.read(data, 0, data.length);
return data;
}

我们读取的WavInfo结构体,包含采样率,分辨率和声道数已经足够让我们去播放我们读取的音频了。

如果我们不需要将全部音频数据一次性放入内存中,我们可以使用一个InputStream,一点一点地读取。

阅读全文 »

By Long Luo

2014.06.03 Apple举行了万众瞩目的WWDC2014,发布了OS X Yosimite和iOS8,最近几天看了一部分Keynote,从网上搜集了一部分资料,将这次WWDC14的一些新功能点汇总出来,同时加入了自己的一些想法,探讨下码农群体应该注意哪些趋势。

Continuity & Seamlessly

总体来说:

系统功能更加完善,更加人性化,更加开放,与其他iOS、Mac设备整合度扩大,并且预示了未来与可穿戴设备、智能家居的紧密结合的趋势,预计Health和Home两大产业将发生重大变革,将有一系列的新App和硬件改变我们的生活。

WWDC的报道会分别讲解Mac OS Yosemite和iOS 8的各项功能。可是整场看下来,觉得这两个系统的更新都有一个指针:统一。爷就是关键的2个词,Continuity & Seamlessly

Mac OS Yosemite的扁平化和透明统一了Mac OS和iOS的视觉风格。 Mac OS也追加了iOS上特有的功能,比如打电话、IM。

两个系统在视觉和功能上统一的同时,通过 iCloud 的升级,实现了数据同步。 「视觉」,「功能」和「数据」统一了之后, Handoff 实现了Mac OS和iOS工作的无缝切换。

最终,所有“苹果设备”都将无缝的融合到一起。随时拿起iPad,打开Macbook或者掏出 iPhone,你都能看到同样的视觉风格,得到一致的数据,使用同样的功能。

iOS8 新功能点

HealthKit

健康管理软件

这是一个私人健康数据平台,它可以整合其他第三方健康应用数据,也就说可以在HealthKit中统一浏览其他应用监测的数据。同时,它也将与梅奥诊所和其他医疗机构合作,允许医疗机构接受或传输你的数据。

HomeKit

把iPhone变成智能家居中心控制器
可以实现远程遥控智能家居设备,如智能门锁、灯泡或是监控摄像头。操作非常简单,只需对siri说:“我要上床睡觉了”,灯光就会变暗、门就会锁上。

Mail

1. 支持直接从邮件中添加日历事件。
2. 在邮件中将会引入全新的邮件手势,下滑消息可触发新动作。  
3. 图像编辑和草图分享,能够直接通过浏览器来提供强悍的图片编辑能力,并快速进行邮件分享。
4. 支持群组通信。群发功能——你可以直接在会话中添加或移除对象。                                                                                   
5. 轻松切换和快速访问联系人。                                                                   
6. 当你在手机上写了一半邮件之后,还可以在就近的电脑上继续完成  
7. 在当前消息中打开另一封邮件,然后再切换回来(工作进度依然保存着)。                                                                                                           
8. 可以在通知中直接为Facebook上的消息点赞!  
                            

iMessage

变身为一个没有朋友圈和公众号的微信

1. 支持群组通信。你可以直接在会话中添加或移除对象。                      
2. 支持多达14个国家和地区的语言,加快打字速度
3. 发送语音消息的功能很有趣。 
4. 图片支持以“相册”形式快速查看。
5. 支持语音信息,与对话的人共享位置。                                               
6. 支持发短视频                                                                                        
7. 在群聊短信设置中有“不要打扰”的相关选项

优化输入法

1. iOS8中在键盘中能够实现的第三方输入法,QuickType可智能预测。

当用户输入完当前单词之后,QuickType会提供3个智能预测的单词供用户选择。当然,如果你不喜欢这种“输入建议”,也可以轻松移除。

2. 在键盘中还引入了全新的Tap to Talk功能

新的Tap to Talk功能,语音输入短信,用户能够录制一段音频或视频并将其发送给收件人。

阅读全文 »

By Long Luo

2014年的06月01日,去参观了2014年深圳·香港·澳门国际车展,算是第一次参加车展吧,按照惯例谈谈感(tu)想(cao)吧:

一、学到的东西:

English:

车展:Auto Show or Motor Show
手动挡:MT(Manual Transmission)
自动挡:AT(Automatic Transmission)
手自一体:AMT(Automated Mechanical Transmission)
档位:P(Parking) R(Reverse) N(Neutral) D(Drive)

Car Brand:

A

奥迪-Audi 
阿斯顿马丁-Aston Martin   

B

宝马-BMW   
宾利-Bentley   
别克-Buick  
       

C 

凯迪拉克-Cadillac   
雪铁龙-Citroen   
雪佛兰-Chevrolet   

F 

法拉利-Ferrari   
菲亚特-Fiat   
福特-Ford 

H 

本田-Honda   
现代-Hyundai   

J 

捷豹(美洲虎)-Jaguar

K 

起亚-Kia   

L 

路虎(陆虎)-Land Rover   
雷克萨斯-Lexus   
蓝伯基尼-Lamborghini  

M

马自达-Mazda 
梅赛德斯-奔驰-Mercedes-Benz   
玛莎拉蒂-Maserati 
迷你-Mini  

N

日产--Nissan  

P

标致-Peugeot   
保时捷-Porsche   

R

劳斯莱斯-Rolls-royce   

  S 

双龙-Ssangyong   
斯柯达-Skoda   
斯巴鲁-Subaru   
铃木-Suzuki     
精灵-Smart 
阅读全文 »

翻译 By Long Luo

原文链接:iOS Programming Basic: How Does the Hello World App Work?

译者注:
1. 由于这是技术文章,所以有些词句使用原文,表达更准确。
2. 由于水平有效,有些地方可能翻译的不够准确,如有不当之处,敬请批评指正.

我希望你享受了第一个iOS编程教程,同时已经创造了你的第一个App。在进入下一教程以及制作一个更复杂的App之前,我们有必要回过头,分析这个Hello World App。对于你理解一些Objective-C语言的语法和App的内部工作机制有很大帮助。

目前为止,想必你已经按照教程完成了你的第一个Hello World App。不过,当你完成了这个教程之后,你脑海里肯定冒出了更多疑问:

  • xib,.h,.m文件是做什么用的?
  • showMessage内部的代码是什么?用什么作用?
  • 当你按下Hello World的按钮发生了什么呢?按钮是如何触发了显示消息的动作呢?
  • Xcode中的Run按钮是如何运作的?

我希望你已经对Xcode IDE开发环境比较熟悉了,这样我就不用再解释一遍上面的内容了。对于每个开发者来说,理解代码的内部细节和抓住基本概念对于iOS编程是很有必要的。对于某些技术概念,如果你没有丝毫的编程背景来说,理解一些技术概念是有一定难度的。但是,别担心,这里仅仅是一个开始。如果你继续学习后续的教程,写出更多的代码,你就能更好的理解iOS编程。尽你所能努力学习更多知识吧!

Interface Builder, Header and Implementation Files

首先,.xib, .h, .m文件是什么呢?这是一位读者提出的一个非常好的问题。在项目导航中,你应该可以找到3种主要的文件类型:.xib, .h, .m。(如果你打开“Supporting Files”文件夹,你可以找到其他的文件类型,例如plist和framework。但到目前为此,我们先忘掉它们,在今后课程中我们会讨论它们。)

.xib

  • 如果一个文件也有.xib的扩展名,它们是Interface Builder文件,存储了应用的UI。当你点击了.xib文件,Xcode会自动的打开Interface Builder界面,你可以通过拖动和放下来编辑应用的UI。如下图所示:
Interface Builder in Xcode

Interface Builder in Xcode

.h and .m

  • .h扩展名的文件表示这是头文件.m扩展名表示是具体的实现。和其他大多数编程语言一样,Objective-C的源码也分为2部分:接口实现

为了便于你更好的理解这2者关系,我们拿电视遥控器打比方。我们可以很方便地使用无线遥控器调节电视的音量。你按下音量+按钮增大扬声器的音量。切换频道时,你只需要按下频道数字。那我来问问你,你知道当你按下音量按钮的背后发生了什么吗?估计你不知道吧。我相信大部分人都不知道遥控器和扬声器之间是如何通信的。我们仅仅知道的是,那个按钮是用来调节音量的。在这里,按钮就是接口,而按钮之后的具体细节我们称之为实现

现在你应该对接口和实现有了一个更深的理解。让我们回到代码,在Objective-C语言中,一个类的接口是放在.h文件中。我们使用语法标示符@interface来声明一个类的接口。看下HelloWorldViewController.h的具体实现:

@interface HelloWorldViewController : UIViewController

-(IBAction)showMessage;

@end

HelloWorldViewController这个类名以“@interface”开头。内部则声明了一个“showMessage”的实现,也可以称之为方法

就像音量按钮,显然我们不知道showMessage这个方法是如何运作的。你仅仅知道它是用于在屏幕上显示一条信息。具体的实现则放在HelloWorldViewController.m文件中,如下所示:

@implementation HelloWorldViewController

// I've removed other methods for better reading. Focus on the showMessage method first.

- (IBAction)showMessage 
{
    UIAlertView *helloWorldAlert = [[UIAlertView alloc]
                                initWithTitle:@"My First App" message:@"Hello, World!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

    // Display the Hello World Message
    [helloWorldAlert show];
}

@end

正如你上面所示,你使用“@implementation”去声明一个实现。在“showMessage”中,代码用于定义在屏幕中弹出一条警告。你不需要弄明白在“showMessage”的方法中每一行代码具体含义。简单来说,创建了一个以“My First App” 为标题,“Hello, World”作为消息的UIAlertView。然后调用“show”方法去请求iOS用于在屏幕上显示一个弹出消息。如下图所示:

Hello World App
阅读全文 »
0%