《簡易RSSリーダーを作ってみた 〜 Objective-C編 〜》のMacOS X 用サンプルコードをほんの少し改造して、Reuters: トップニュースをKyokoさんに読み上げてもらいました。
テキストの読み上げ
OS X の Command Line Tool でテキストの読み上げを行うためには、AppKit をインポートして NSSpeechSynthesizer を使います。
#import <AppKit/AppKit.h>
NSSpeechSynthesizer の生成・初期化は以下のような感じで音声、読み上げ速度、音量を指定します。
NSSpeechSynthesizer *speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
[speechSynth setVoice:@"com.apple.speech.synthesis.voice.kyoko"];
[speechSynth setRate:240];
[speechSynth setVolume:0.9];
読み上げは startSpeakinkString で開始して、一旦スリープ(しないと読み上げが開始されない模様)、読み上げが完了して isSpeaking の値がNOになるまで待ちます (ちなみに、Cocoaアプリだと読み上げが終わると didFinishSpeaking が呼び出される)。
[speechSynth startSpeakingString:title];
sleep(1);
while([speechSynth isSpeaking] == YES) {
usleep(1000);
}
テキストの抽出
RSSフィードの記事の中にはリンク情報なども含まれているので、それらをそのままNSSpeechSynthesizerに渡すと、意味不明な呪文の詠唱が行われてしまいます。それでは困るので(description)を NSAttributedStringを使って取得した記事から読み上げ可能なテストを抽出します。
NSData *data = [description dataUsingEncoding:NSUTF16StringEncoding];
NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:data options:nil documentAttributes:nil];
サンプルコード
まあ、こんなものでしょうか(手抜き)。
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#define PATH @"http://feeds.reuters.com/reuters/JPTopNews"
int speech_rss(NSXMLDocument*);
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Download RSS feed.
NSURL *url = [NSURL URLWithString:PATH];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadRevalidatingCacheData
timeoutInterval:30];
NSData *urlData;
NSURLResponse *response;
NSError *error;
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
if (!urlData) {
printf("failed to url request.");
}
//NSString* xmlString = [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
//NSLog(@"doc\n%@\n", xmlString);
// Create XML Document fron urlData
NSXMLDocument *doc;
doc = [[NSXMLDocument alloc] initWithData:urlData
options:0
error:&error];
if (!doc) {
printf("failed to create xml doc.");
return 1;
}
// speech RSS Feed.
speech_rss(doc);
return 0;
}
}
int speech_rss(NSXMLDocument *doc) {
NSError *error;
// Initialize speech Synthesizer.
NSSpeechSynthesizer *speechSynth = [[NSSpeechSynthesizer alloc] initWithVoice:nil];
[speechSynth setVoice:@"com.apple.speech.synthesis.voice.kyoko"];
[speechSynth setRate:240]; // default value : 180.0
[speechSynth setVolume:0.9]; // default value : 1.0
//NSLog(@"speechSynth: %f %f %@", [speechSynth rate], [speechSynth volume], [speechSynth voice]);
// Get and print Title of RSS Feed.
NSArray *channelNodes = [doc nodesForXPath:@"//channel" error:&error];
if (!channelNodes) {
return -1;
}
for (NSXMLElement* element in channelNodes) {
if ([element kind] != NSXMLElementKind) {
continue;
}
NSArray* children = [element children];
NSString* title;
for (NSXMLNode* node in children) {
if ([[node name] isEqualToString:@"title"]) {
title = [node stringValue];
printf("\nTitle: %s\n\n", [title cStringUsingEncoding:NSUTF8StringEncoding]);
[speechSynth startSpeakingString:title];
sleep(1);
while([speechSynth isSpeaking] == YES) {
usleep(1000);
}
}
}
}
// Get Node list of RSS items.
NSArray *itemNodes = [doc nodesForXPath:@"//item" error:&error];
if (!itemNodes) {
return -1;
}
for (NSXMLElement* element in itemNodes) {
if ([element kind] != NSXMLElementKind) {
continue;
}
NSArray* children = [element children];
NSString* title;
NSString* description;
for (NSXMLNode* node in children) {
if ([[node name] isEqualToString:@"title"]) {
title = [node stringValue];
} else if ([[node name] isEqualToString:@"description"]) {
description = [node stringValue];
}
}
// Extract readable text.
printf("title: %s\n", [title cStringUsingEncoding:NSUTF8StringEncoding]);
NSString* speechText = nil;
NSData *data = [description dataUsingEncoding:NSUTF16StringEncoding];
NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:data options:nil documentAttributes:nil];
if (as) {
speechText = [[NSString alloc] initWithFormat:@"%@ \n %@ ", title, [as string]];
printf("article: %s\n", [[as string] cStringUsingEncoding:NSUTF8StringEncoding]);
} else {
speechText = [[NSString alloc] initWithFormat:@"%@ \n %@ ", title, description];
printf("article: %s\n", [description cStringUsingEncoding:NSUTF8StringEncoding]);
}
// Speach extracted text.
[speechSynth startSpeakingString:speechText];
sleep(1);
while([speechSynth isSpeaking] == YES) {
usleep(10000);
}
usleep(10000);
}
return 0;
}
デモ
たぬきぷろじぇくとでは、これを使ってRSSフィードを読み上げるアプリを作っています (O▽O)