hylom'sWeblog

- hylom's blog
| about hylom / hylom.net | old contents | login

Raspberry Piを使ってSpotifyをheadlessで聞く

posted on 2018/03/21 17:23:26

 Raspberry Piを使ってSpotifyを再生できるデバイスを作ったので手順などを備忘録代わりに。使用したデバイスはRaspberry Pi Zero W。OSはRaspbian(Stretch)。なお利用にはSpotifyの有料アカウントが必須。

Spotify Connectデバイスとして使用する

 Spotifyを再生するためには、デバイス側でSpotifyにログインしてプレイリストなどの情報を取得して再生する、という処理を全部行うやり方と、PC/Macやスマートフォン、タブレットなどのSpotifyアプリ側で認証やプレイリスト/楽曲管理などを行い、Spotifyとの通信およびデータダウンロード、再生などのみをデバイス側で行う「Spotify Connect」という機能を使うやり方がある。

 Spotify Connectデバイスとして利用する場合、Raspotifyというソフトウェアを利用すると簡単に実現できる。librespotというオープンソースで開発されているSpotifyクライアントライブラリが使われているが、これらを含有したRaspberry Pi向けのバイナリパッケージが配布されているので、これをインストールし、設定ファイルを編集してサービスを起動するだけで良い。

 設定ファイルは/etc/default/raspotify。変更したのはデバイス名を指定する「DEVICE_NAME」、ビットレートを指定する「BITRATE」といくつかのオプション。オプションについてはlibrespotのドキュメントにまとめられているが、ここでは使用するALSAデバイスを指定する「--device」、Spotifyアプリとの接続に使用するポートを指定する「--zeroconf-port」、キャッシュディレクトリを指定する「--cache」、音量の自動調整を有効にする「--enable-volume-normalisation」、初期音量を指定する「--initial-volume」を使用している。--deviceの引数はALSAの設定によって変わるので適宜適切なものを選択(後述)。ユーザー名などはSpotifyアプリ経由で取得するので設定不要。「--zeroconf-port」は指定しなくても良いのだが、指定するとポートを固定できるためファイアウォールの設定が簡単になる。

# /etc/default/raspotify -- Arguments/configuration for librespot

# Device name on Spotify Connect
#DEVICE_NAME="raspotify"
DEVICE_NAME="5potpy"

# Bitrate, one of 96 (low quality), 160 (default quality), or 320 (high quality)
#BITRATE="160"
BITRATE="320"

# Additional command line arguments for librespot can be set below.
# See `librespot -h` for more info. Make sure whatever arguments you specify
# aren't already covered by other variables in this file. (See the daemon's
# config at `/lib/systemd/system/raspotify.service` for more technical details.)
#
# To make your device visible on Spotify Connect across the Internet add your
# username and password which can be set via "Set device password", on your
# account settings, use `--username` and `--password`.
#
# To choose a different output device (ie a USB audio dongle or HDMI audio out),
# use `--device` with something like `--device hw:0,1`. Your mileage may vary.
#
#OPTIONS="--username <USERNAME> --password <PASSWORD>"
OPTIONS="--device mydev --zeroconf-port 65432"

# Uncomment to use a cache for downloaded audio files. Cache is disabled by
# default. It's best to leave this as-is if you want to use it, since
# permissions are properly set on the directory `/var/cache/raspotify'.
CACHE_ARGS="--cache /var/cache/raspotify"

# By default, the volume normalization is enabled, add another volume argument
# here if you'd like, ie `VOLUME_ARGS=--initial-volume 100`.
VOLUME_ARGS="--enable-volume-normalisation --initial-volume 100"

 設定ファイルを作成したら、systemctlコマンドでサービスを開始できる。

# systemctl start raspotify

 ログはjournalctlコマンドで閲覧できる。

# journalctl -u raspotify

 確認していないが、zeroconfを使っているとのことで別途avahi-daemonも必須かもしれない。

# systemctl start avahi-daemon

Webブラウザ経由で操作できるSpotifyクライアントを使う

 基本的にはSpotify Connect経由での利用で事足りるのだが、Webブラウザ経由で操作できるクライアントについても試しに導入してみた。こちらはMopidyというミュージックサーバーソフトウェアとSpotify用のプラグインを組み合わせて利用することで実現できる・

 Mopidy自体はRaspbian標準でパッケージが提供されているので、これをインストールする。

# apt-get install mopidy

 次に、Spotify用のプラグインをインストールする。必要なのは下記。

  • mopidy-mopify:MopidyにSpotify連携用のWeb UIを追加するプラグイン
  • mopidy-spotify:MopidyにSpotifyからのストリーミング再生機能を追加するプラグイン
  • libspotify:かつてSpotifyが公式に提供していたSpotify連携ライブラリ。すでに開発終了でいつサポートが終了してもおかしくない
  • pyspotify:libspotifyのPythonラッパー

 libspotifyとコンパイル済みのpyspotifyはMopidyのaptリポジトリから入手可能(ドキュメント)。自分は次のように直接ダウンロードして使用した。

$ wget http://apt.mopidy.com/dists/jessie/non-free/binary-armhf/libspotify12_12.1.103-0mopidy1_armhf.deb
$ wget http://apt.mopidy.com/dists/jessie/contrib/binary-armhf/python-spotify_2.0.5-0mopidy1_armhf.deb

 いくつか依存するパッケージがあるので、それはapt-getでインストールする。mopidy-spotifyとmopidy-mopifyについてはpipでインストールできる。

# pip install Mopidy-Spotify
# pip install Mopidy-Mopify

 続いて設定ファイルの編集。

[core]
cache_dir = /var/cache/mopidy
config_dir = /etc/mopidy
data_dir = /var/lib/mopidy

[logging]
config_file = /etc/mopidy/logging.conf
debug_file = /var/log/mopidy/mopidy-debug.log

[local]
media_dir = /var/lib/mopidy/media

[m3u]
playlists_dir = /var/lib/mopidy/playlists

[http]
enabled = true
hostname = 192.168.1.210
port = 6680
zeroconf = Mopidy HTTP server on $hostname

[mopify]
enabled = true
debug = false

[spotify]
enabled = true
username = <Spotifyのユーザー名>
password = <Spotifyのパスワード>
client_id = <client ID>
client_secret = <client secret>
bitrate = 320

 hostnameやusername、passwordは各自適切な物を指定する。client_idおよびclient_secertはMopidyのWebサイトにアクセスして「LOG IN WITH SPOTIFY」リンクをクリックしてOAuth認証を行うと、このページにリダイレクトされて設定すべきclient_idおよびcluent_secretが表示される。これをコピペすればOK。

ALSAの設定

 RaspotifyもMopidyもサウンド出力にはALSAを使用する。認識されているデバイスは/proc/asound/cardsファイルで確認できる。たとえば次の例では、デバイス0としてRaspberry Pi内蔵オーディオ(Raspberry Pi Zero Wの場合HDMI経由での出力に相当するはず)、デバイス1としてUSB端子に接続したオーディオIFが認識されている。

# cat /proc/asound/cards
 0 [ALSA           ]: bcm2835 - bcm2835 ALSA
                      bcm2835 ALSA
 1 [USB            ]: USB-Audio - <USBサウンドデバイスの情報がここに表示される>

 ALSAの場合、各アプリケーションが使用するサウンドデバイスに関する設定は/etc/asound.confで行える。今回はデフォルト設定(pcm.!default)に加えて、「pcm.mydev」という設定を作ってこれをRaspotifyで使用するよう指定している(前述)。dmixerの設定ではuidが異なる複数のプロセスからの非排他的音声出力を可能にするため「ipc_key_add_uid 0」「ipc_perm 0660」の2つを指定している。また、「slave」以下で「hw:1」を指定することで、オーディオデバイス1、つまりUSBサウンドデバイスを使用するよう指定している。

# cat /etc/asound.conf 
pcm.mydev {
  type plug
  slave {
    pcm "dmixer"
  }
}

pcm.!default {
  type plug
  slave {
    pcm "dmixer"
  }
}

pcm.dmixer {
  type dmix
  ipc_key 1024
  ipc_key_add_uid 0
  ipc_perm 0660
  slave {
    pcm "hw:1"
  }
}

問題点

 Mopidyでは一部のプレイリストが適切に取得できなかったりする模様。これはlibspotifyがもうdeprecatedであるためらしい。

 また、MopidyとRaspotify両方で発生する問題だが、ネットワークが遅くなる時間帯だとストリームのキャッシュに時間がかかったり、間に合わずに音切れが発生する状況となっている。Spotifyアプリだとこの問題は発生しないのでパケットを調べたりしたのだが、どうもSpotify公式アプリはakamai経由でコンテンツをダウンロードしているため混雑時でも高速にコンテンツをダウンロードできているようだ。この機能はMopidy(libspotify)やlibrespotではサポートされていないようなので、簡単には解決できなさそうである。

SELinuxを有効にしている場合にDockerコンテナからホストのファイルシステムにアクセスできない場合の対処法

posted on 2018/03/07 17:45:42

 dockerでローカルのファイルシステムをコンテナ内にマウントして利用するとき、パーミッションは適切なはずなのにそのディレクトリにアクセスできないトラブルに遭遇(環境はFedora 25)。

 とりあえず、SELinuxの状況を確認。

# getenforce
Enforcing

 有効となっていたので、ログ(/var/log/audit/audit.lo)を確認。次のように対象ディレクトリ(ここでは「hal」)に対するログが見つかった。つまり、SELinuxによってアクセスがブロックされていたということ。

type=AVC msg=audit(1520416357.374:153155): avc:  denied  { write } for  pid=18595 comm="java" name="hal" dev="dm-0" ino=398292 scontext=system_u:system_r:container_t:s0:c825,c930 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=dir permissive=0a

 細かくSELinuxのルールを設定するのが適切なのだが、今回はテスト環境なので次のようにしてcontainer_tに関するアクセス制御を全部無効にすることで対処。

# semanage permissive -a container_t

「start-limit-hit」といったメッセージが出てdocker serviceの起動に失敗した場合の対処法

posted on 2018/03/07 17:14:13

 systemdでdocker serviceの起動時に、次のようなエラーが出て起動できない場合がある(このメッセージはjournalctl -xeコマンドで確認)。

-- The result is failed.
 3月 07 18:28:10 fedora25 systemd[1]: docker.service: Unit entered failed state.
 3月 07 18:28:10 fedora25 systemd[1]: docker.service: Failed with result 'start-limit-hit'.

 このエラーは使用しているdockerのバージョンとsystemdのdocker.service設定ファイルの内容が合っていない場合に出るようだ。このエラーが出た環境は、Dockerが配布しているパッケージでDockerをインストール後、そのパッケージを削除してFedora 25標準のパッケージに入れ直したというもの。自分で作成して忘れていたのか、それともパッケージによってインストールされたのか分からないが、/etc/systemd/systemディレクトリ以下にdocker.serviceファイルが残っていたのが原因。

 このファイルを削除し、systemctl daemon-reloadでsystemdの設定を読み直したら復旧した。

小型のドキュメントスキャナ「EPSON DS-310」導入レポート

posted on 2017/12/17 21:41:14

 家の本棚を整理するため、ドキュメントスキャナを買いました。ドキュメントスキャナとしては富士通のScanSnap(現行のものだとiX500が定番ですが、筐体サイズが大きくそれなりに保管場所を取るということで一回り小さいタイプのものを導入することに。このタイプのものは各社から発売されていますが、カラー300dpi時の読み取り速度でカタログスペックを比較すると富士通のScanSnap S1300iは6枚/分、キヤノンのDR-P215IIも6枚/分なのに対し、EPSONのDS-310は25枚/分と、大型のScanSnap iX500と同等の読み取り速度となっており、十分に実用的だと判断してこちらを購入しました。

 なお、現在キャンペーン中で若干お安く買えるようです。自分はキャンペーン前に購入したので1000円ちょっと割高に買ってしまいました……。

 実際に何冊かスキャンを行ってみましたが、200ページ前後の雑誌のスキャンが5分ほどで完了します。速度に関しては十分に実用的と感じました。ただコンパクト型の宿命か、給紙トレイが小さく同時にセットできる原稿枚数は少なめです。用紙の厚みにもよりますが十数枚程度が限界のようで、給紙トレイに原稿を多く入れすぎると簡単に二重に重なった状態で給紙されてしまいます。ただ、適切な枚数をセットした状態であれば二重送りはほぼ発生しないと思われます。また、給紙トレイの側面のガイドが小さめなのがちょっと不安ではあります。薄めの紙だと斜めに給紙されてしまいがちな気はします。

 スキャン品質については、300dpi・カラーでは特に不満はないレベルです。付属のスキャンソフトについてもJPEG形式でスキャンするぶんにはとりあえず不満はありません。

 あとは、付属するUSBケーブル(USB 3.0、MicroB)が短め(約1m)なのでPCから離れた場所に設置したい場合は長いケーブルを別途用意する必要があります。USBからの給電だけでも動作しますが、スキャン速度が一気に落ちるのでほぼACアダプタでの給電は必須でしょう。

 ちなみに、裁断機にはPLUSのスライドカッター ハンブンコというのを使っています。

 中とじの雑誌の針を外してバラした後、これで裁断という作業をしていますが、正確に半分にカットできるわけではないものの、おおむね問題なく裁断できています。ただ、刃の下に入る紙束の厚さは6〜7mm程度なので、文庫本や漫画など平とじの厚い本をそのまま裁断するのは難しいと思います。

玄人志向の2.5インチ×2が入るHDDケースをMacで使う

posted on 2017/11/07 23:41:10

 自宅のiMacにはバックアップ(Time Machine)用、作業用、音楽/写真用と3つの外付けHDD(一部SSD)をつなげているのだが、さすがに3台をぶら下げてると周辺がごちゃごちゃするので、その一部をまとめようと複数のHDDを組み込めるHDDケースを導入することにした。

 今回導入したのは、玄人志向のUSB3.1接続 2.5型 SATA HDD/SSD x2 ケース GW2.5ACX2-U3.1AC

 複数のHDDを搭載できるHDDケースはそれなりに選択肢があるが、2.5インチ専用なのは選択肢が少ない。その中で最も安価だったのがこれだった。ただし、Webサイトでは対応OSがWindows 7〜10となっており、Macで動作するかの記載はなし。RAIDではなく2台のHDDを個別に認識するモードであれば問題ないだろうと判断して購入。価格はヨドバシカメラで6,510円(税込)。

 実際に試してみた結論から言うと、問題なく利用できた。また、マニュアルには「Mac OS環境下ではスリープモード、電源連動が正しく動作しない可能性があります」とも記載されているが、自分の環境(iMac Retina 4K 21.5inch 2017)ではスリープ時電源が入りっぱなしではあるものの、スリープ後も問題なく復帰した。2台のHDDを個別に認識する「SINGLE」モードでの検証で、RAIDの動作は確認していない。

 2017版iMacはUSB Type-Cポートが搭載されており、USB3.1 Gen.2に公式に対応している。そのため、システム的には10Gbpsで接続されていることが確認できる(付属のUCB Type-Cケーブルを使用)。

 ちなみに付属のACアダプタを接続しなくても、USBで接続しただけで電源が入りHDDが認識された。もしかしたらUSB経由での電源供給だけで動作するかもしれない。ただ、説明書にはACアダプタを使えと書いてあるので念のためACアダプタを使用している。

 3.5インチのHDDケースと比較すると体積は半分程度。これで2.5インチHDDを2発入れられるのは非常に良い。値段も2.5インチのHDDケース2個分+アルファ程度なので、今のところかなり満足度は高い。

Google Adsense/Adwords公式サイトのように偽装したフィッシングサイトにご注意を

posted on 2017/10/06 17:45:26

 Google Adsenseの管理画面にログインしようとしたところ、あやうくフィッシングサイトに引っかかりそうになったので注意喚起。

 Google検索で、「google adsense」と検索すると「google.com - Log In - Sign Up」という広告が出ました。「広告」という表示とともに、ドメインとして「adwords.google.com」が表示されているので、これだけを見るとGoogleによる正規の広告のように見えます(なお、現時点では対処されたのかすでに表示されなくなっています)。

 ところが、この広告をクリックすると「adwords-google-website.com」というドメインのサイトに飛ばされます。このサイトやログイン画面はGoogleのものに似ていますが、よく見るとSSL接続ではありませんし、サインイン画面も非SSL接続。Googleでこれはあり得ません。

 このサイトのドメイン情報をWHOISで調べて見ると、登録者組織は記入されておらず、連絡先メールもyandex.comドメイン。Googleとは関係なさそうです。

 はてなブックマークコメントによると、どうもAdWordsでは表示するURLを任意に指定できてしまうようです。

 また、このサイトにメールアドレスとパスワードを入力すると、それに加えて登録している電話番号の入力も求められます(架空のアカウント情報を使って検証しています)。

Mastodon入門書(電子書籍)を書きました

posted on 2017/08/03 23:26:02

 SBクリエイティブから、「Mastodonスタートアップガイド」という電子書籍を出すことになりました。すでに各電子ストアで販売されております。Amazon.co.jpでの商品ページはこちら。8月16日までキャンペーン中で270円で購入可能です。

 そのほかiBooksやkobo、Yahoo!ブックストア等でも購入できるようです(SBクリエイティブのキャンペーンページ)。

 通常価格は500円+税になるかと思います。一般ユーザーの入門書ということで、技術的な話はほとんどありません。無料で読める「立ち読み版も用意されております。

 低価格・一般向け・低価格の電子書籍タイトルを増やしたいとのことで執筆したのですが、当初の話では1万文字程度という話だったのに書き終わったら2万文字ほどになっており、お値段も500円超という形に。とりあえず何も分からないけどMastodonって何?という方が読むとある程度知ったかぶりができる内容となっております。

Ansibleのdocker_serviceモジュールのトラブルシューティング

posted on 2017/06/08 18:12:04

 Ansibleにはdocker_serviceというDockerを操作するためのモジュールがあるのだが、「preview」というステータスであるため現状(2017-06-08現在)色々と地雷が埋まっている。その最たるものがまともなエラーメッセージを吐いてくれないというもの。1月時点でIssueとして挙がっているのだが(#20480)、実行してDocker関連のエラーが出た場合に出るメッセージが「Error starting project」のみ。

 この問題を解決するプルリクエストも出ているのだが、現時点ではAnsibleのリリース版にはマージされていない。

 ということで、暫定的な対応としてプルリクエストを出した作者のリポジトリからdocker_service.pyを直接ダウンロードしてインストールされているものと置き換えて使うとトラブルシューティングが捗るかと思われます。

AVFoundation小ネタ1:AVAssetを使ってmp3やm4aのタグ情報を取得する

posted on 2017/05/21 01:28:53

 macOS(10.7以降)やiOS(4以降)などで利用できるAVAssetクラスを利用することで、別途外部のライブラリを使用せずにmp3ファイルやm4a(AAC)ファイルのメタデータ(タグ情報)を取得できる。AVAssetはさまざまな動画や音声ファイルに対応しており、同様のやり方で動画ファイルのメタデータも取得できると思われる(未検証)。

 特定のファイルのメタデータを取得するには、まずAVAsset(URL: url)メソッドを使ってAVAssetオブジェクトを作成する。url引数には対象ファイルのパスをNSURL形式で指定する。

let path = <ファイルのパス>
let url = NSURL(fileURLWithPath: path)
let asset = AVAsset(URL: url)

 メタデータにはmetadataプロパティやcommonMetadataプロパティ経由でアクセスできる。このプロパティはAVMetadataItemの配列となっている。この配列に対しfor inループなどを使ってメタデータを探して処理を行えば良い。

for metadata in asset.metadata {
    // metadataオブジェクトを使ってメタデータを得る
}

AVMetadataItemの中身 

 ここまでは簡単なのだが、AVMetadataItemクラスはさまざまな形式のファイルに対応するための抽象化がされており、ドキュメント初見では意味が分かりにくいかもしれない。

 まずkeySpaceプロパティだが、ここにはそのメタデータがどのファイル形式でサポートされているものなのかを示す文字列が格納されている。ここで使われる文字列のうちいくつかはあらかじめ定数として定義されている(Key Spaces)。たとえばmp3が格納しているタグ情報であるID3で利用できるメタデータの場合、keySpaceプロパティの値はAVMetadataKeySpaceID3(文字列としては"org.id3")になる。AVMetadataItemクラスには、指定したメタデータの配列から指定したkeySpaceに対応するメタデータだけを取り出すmetadataItemsFromArrayというクラスメソッドも用意されている。

 また、keyプロパティにはメタデータを識別する情報(キー)が格納されている。この情報は必ずしも文字列であるとは限らないため、型はStringではなく(NSCopying & NSObjectProtocol)?になっている。そのため、これを文字列として参照したい場合はidentifierプロパティを使う。

 文字列形式のメタデータキーについても定数があらかじめ定義されている。たとえばID3の場合、タイトルはAVMetadataID3MetadataKeyTitle、アルバム名はAVMetadataID3MetadataKeyAlbumTitleという定数が定義されている。たとえばタイトルとアルバム名を取り出したい場合、これを使って次のようにすれば良い。

for metadata in AVMetadataItem.metadataItemsFromArray(asset.metadata, withKey: nil, keySpace: AVMetadataKeySpaceID3) {
    guard let key = metadata.identifier else { continue }

    if key == AVMetadataID3MetadataKeyTitle {
        if let v = metadata.stringValue {
            title = v
        }
    }
    if key == AVMetadataID3MetadataKeyAlbumTitle {
        if let v = metadata.stringValue {
            album = v
        }
    }
    :
    :
}

 値が格納されているvalueプロパティについても型はStringではなく(NSCopying & NSObjectProtocol)?になっているので、stringValueやnumberValue、dateValueなどのメソッド経由で取り出すのが良い。

 さて、ファイル形式によって利用できるkeyは異なる。たとえば同じタイトルでも、ID3ならAVMetadataID3MetadataKeyTitleDescription、iTunesならAVMetadataiTunesMetadataKeySongNameというキーになっている。種別ごとにキーが異なるのは面倒なので、AVMetadataItemにはcommonKeyというプロパティが用意されており、たとえばタイトルを示すメタデータであればこのプロパティにAVMetadataCommonKeyTitle(文字列としては"title")というデータが入っている。こちらもあらかじめ定数が定義されている。こちらを利用することで、ファイル形式によらず情報を取得できる。

for metadata in asset.metadata {
    if metadata.commonKey == AVMetadataCommonKeyTitle {
        if let v = metadata.stringValue {
            title = v
    }
}

iTunes形式のメタデータに関する注意点

 iTunes形式のメタデータ(keySpaceがAVMetadataKeySpaceiTunesのメタデータ)では、identifierプロパティに「itsk/%A9nam」といった文字列が格納されている。いっぽう、あらかじめ定義されているiTunes Metadata Keysでは、たとえばタイトルを示すAVMetadataiTunesMetadataKeySongNameの値は「@nam」という文字列になっており、これらが一致しない。そのため、次のようにしてidentifierの値を変換した上で比較する必要がある。

guard let key = metadata.identifier else { continue }
// key is like 'itsk/%A9nam', so split and decode
let comps = key.componentsSeparatedByString("/")
    if (comps[0] != "itsk") {
        continue
    }
    let k = comps[1].stringByReplacingOccurrencesOfString("%A9", withString: "@")
}

Mac OS X向けEmacs 25.1で日本語入力時にちらつく問題の原因

posted on 2017/04/17 00:26:43

 Emacs for Mac OS Xで配布されているEmacsのMac OS X向けビルドでは、Emacs 24.4移行は標準でインラインでの日本語変換が可能になっている。これはEmacs 25.1でも同じだが、日本語入力時にカーソルがちらつく(1文字入力したり、変換するたびにカーソルが変換位置の先頭に一瞬移動してその後変換位置の最後に移動する)という現象が発生する。この問題はEmacs 24.5では発生していなかったので、24.5から25.1の間のどこかで混入したと思われる。そこで、EmacsのGitリポジトリをたどってこの問題がどこで発生するようになったのかを検証してみた。

 結論から言うと、9e77c1b7bcfd0807be7fe67daf73c2320e864309のコミットで問題が発生するようになっている。

 また、その1つ前のec10ba2792eef613caf47fff83e869d4bc177616のコミットでは問題は確認できなかった。

 問題のコミットで変更されているのはsrc/keyboard.cだったのがちょっと意外。てっきりMac OS X向けのコードであるns*.mあたりが原因だと思っていたのだが。おおむね問題の発生理由は想像できたのだが修正コードはもう少し周囲をチェックする必要があるのでまた今度。

 ちなみにこのコミットを見つけるために行った作業の流れだが、基本的にはいわゆる2分探索である。gitにはこの作業を自動で行うgit bisectコマンドがあるのだが、コンパイルしたバイナリに問題があるかどうかを自動で判別する手段がなかったのでビルド後にバイナリを実行して目視で問題が存在しているかをチェックしなければならなかったのが大変だった。

 (追記@5/21)パッチを作成して公開した。また、このパッチを利用せずとも(少なくともバージョン25.1および25.2では)この問題は以下のようにredisplay-dont-pause変数をnilに設定することで解決できることも発見した。

(setq redisplay-dont-pause nil)

 ただし、この変数はバージョン24.5でobsoleteとなっているので今後も利用できるかは分からない。また、Emacsのバグトラッカーに問題が報告されていたのでこれにコメントを入れておいた。