NHKラジオ語学講座のストリーミングデータをMP3形式で自動ダウンロードする(XMLフォーマット対応版)

※2024年9月になってこのXML版は動作はしなくなりました。現在はJSON版をアップデートしていますので、XML版をご利用の方はメンテナンスを継続しているJSON版への切り替えをお願いいたします。

英会話能力の維持のために、NHKラジオ英会話をmp3化してスマホにダウンロードし、通勤途中などに聞くことを日課にしています。

ラジオ英会話はテレビやネットとは違い、聞いて理解してもらう形で構成されているため、通勤時の流し聞きにぴったりです。また、1日15分、1週間で1時間(金曜は月-木の復習回)なので、毎日1週間分を繰り返し聞くのもちょうど良いです。

ダウンロードするためにはいくつかのフリーソフトがありますが、私の場合はMacのアプリ形式で利用可能なラジリンガルを利用してダウンロードしていました。

しかし、最近はダウンロードするためのファイルを指定するXMLファイルが期待するフォーマットで登録されていないことにより、ラジリンガルを含む多くのツールでダウンロードできない事態が頻発しています。

ダウンロード可能な期間は1週間だけのため、ツールの対応を待たずに手動でもダウンロードできるようにしておかないと落ち着きません。

そこでファイルのダウンロードの手順を調べ、自分に必要な機能のみを実装し、ダウンロードの完全自動化を実現したシンプルなPythonスクリプトを作成したので、ここにメモしておきます。

ストリーミングデータを手動でダウンロードする

自動化のためには、まず手動で最小手順を確認するのが常道です。ダウンロードのための最小手順を以下に示します。

ラジオ英会話のストリーミングデータのファイル名を取得する

まず、下記ファイルへアクセスします。

すると、下記のようなXMLファイルがダウンロードできます。各”music”タグ内のhdateに放送日、file属性にファイル名、dir属性にダウンロードURLの補完用ディレクトリパスがあるので、この組み合わせをメモします。

<musicdata>
<music title="ラジオ英会話" hdate="10月31日放送分" kouza="ラジオ英会話" code="4235516" file="22-er-4235-516" dir="mp4" nendo="2022" pgcode="F0"/>
<music title="ラジオ英会話" hdate="11月1日放送分" kouza="ラジオ英会話" code="4235517" file="22-er-4235-517.mp4" nendo="2022" pgcode="F0"/>
<music title="ラジオ英会話" hdate="11月2日放送分" kouza="ラジオ英会話" code="4235518" file="22-er-4235-518.mp4" nendo="2022" pgcode="F0"/>
<music title="ラジオ英会話" hdate="11月3日放送分" kouza="ラジオ英会話" code="4235519" file="22-er-4235-519.mp4" nendo="2022" pgcode="F0"/>
<music title="ラジオ英会話" hdate="11月4日放送分" kouza="ラジオ英会話" code="4235520" file="22-er-4235-520.mp4" nendo="2022" pgcode="F0"/>
</musicdata>Code language: HTML, XML (xml)

各種ダウンロードソフトによるダウンロードが失敗する原因は、file属性が“<ファイル名>.mp4”のフォーマットに準拠していないことや、dir属性のパラメータの変更に合わせてダウンロード元のURLが変更されることに起因しているようです。上記では10月31日の部分でファイル名の拡張子がないために、ダウンロードができなくなっていたようでした。

各番組のストリーミングをmp3ファイルに変換する

ストリーミングファイルのダウンロードにはffmpegを使用します。ffmpegで下記のコマンドラインオプションで起動するとmp3ファイル形式でダウンロードできます。

ffmpeg -http_seekable 0 -i https://vod-stream.nhk.jp/gogaku-stream/<'dir'属性のフォルダ名>/<'file'属性の拡張子無しファイル名>/index.m3u8 -c:a mp3 "<mp3のダウンロードパス>"
Code language: HTML, XML (xml)

下記はコマンドラインの設定例になります。

ffmpeg -http_seekable 0 -i https://vod-stream.nhk.jp/gogaku-stream/mp4/22-er-4235-516/index.m3u8 -c:a mp3 ".¥ラジオ英会話 2022年10月31日放送分.mp3"Code language: JavaScript (javascript)

上記コマンドラインはffmpeg 4.4.3と5.1.2で動作を確認しています。

ダウンロード処理を自動化する

上記のダウンロード処理を自動化した方法が下記になります。

各語学番組の自動ダウンロード用Pythonスクリプト

自動化にはPythonを選びました。Windows/Mac/Linux上で同じファイルで同じ処理をさせることができることと、読みやすく修正しやすいスクリプトを書くことができるためです。下記スクリプトをUTF-8で保存し実行することで、ファイルの変更なしでWindows/Mac/Linux上のPython 3.9で動作することを確認済みです。

import os
import re
import urllib.request
import xml.etree.ElementTree as ET
import subprocess
import datetime
from enum import auto
from os.path import expanduser
import sys

def main():
    #OS(実行環境)依存のパラメータをセットする
    if sys.platform=='win32': #Windows
        path_delimiter="\\"
        today=datetime.date.today()
        download_dir=".\\download_" + "{0}_CW{1}".format(str(today.year),str(today.isocalendar()[1]).zfill(2))
        ffmpeg_bin=".\\win\\ffmpeg.exe"
    elif sys.platform=='darwin': #Mac
        path_delimiter="/"
        today=datetime.date.today()
        download_dir=expanduser("~")+"/Downloads/NHK語学講座"+"{0}_CW{1}".format(str(today.year),str(today.isocalendar()[1]).zfill(2))
        ffmpeg_bin="./mac/ffmpeg"
    else: #Linux(Synology-NAS)
        path_delimiter="/"
        download_dir="/volume1/music/NHK語学講座"
        ffmpeg_bin="/volume1/@appstore/ffmpeg/bin/ffmpeg"

    #各語学講座のlistdataflv.xmlのURL、講座名、ダウンロード完了済みかどうかをチェックするファイルサイズを定義する
    url_kouza_sizes = []
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/basic0/listdataflv.xml',     '小学生の基礎英語',             4801300 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/basic1/listdataflv.xml',     '中学生の基礎英語【レベル1】',   7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/basic2/listdataflv.xml',     '中学生の基礎英語【レベル2】',   7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/basic3/listdataflv.xml',     '中高生の基礎英語 in English',  7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/kaiwa/listdataflv.xml',      'ラジオ英会話',                 7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/vr-radio/listdataflv.xml',   'ボキャブライダー',             2407200 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/enjoy/listdataflv.xml',      'エンジョイ・シンプル・イングリッシュ', 7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/timetrial/listdataflv.xml',  '英会話タイムトライアル',       4801300 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/english/business1/listdataflv.xml',  'ラジオビジネス英語',           7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/french/kouza/listdataflv.xml',       'まいにちフランス語【初級編】',  7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/french/kouza2/listdataflv.xml',      'まいにちフランス語【応用編】',  7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/italian/kouza/listdataflv.xml',      'まいにちイタリア語【初級編】',  7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/italian/kouza2/listdataflv.xml',     'まいにちイタリア語【応用編】',  7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/german/kouza/listdataflv.xml',       'まいにちドイツ語【初級編】',    7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/german/kouza2/listdataflv.xml',      'まいにちドイツ語【応用編】',    7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/spanish/kouza/listdataflv.xml',      'まいにちスペイン語【初級編】',  7201000 ]]
    url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/spanish/kouza2/listdataflv.xml',     'まいにちスペイン語【応用編】',  7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/russian/kouza/listdataflv.xml',      'まいにちロシア語【初級編】',    7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/russian/kouza2/listdataflv.xml',     'まいにちロシア語【応用編】',    7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/chinese/kouza/listdataflv.xml',      'まいにち中国語',               7201000 ]]
    # url_kouza_sizes += [['https://www.nhk.or.jp/gogaku/st/xml/hangeul/kouza/listdataflv.xml',      'まいにちハングル講座',          7201000 ]]

    # ダウンロード先のフォルダがない場合はフォルダを作成する
    os.makedirs(download_dir, exist_ok=True)

    #各語学講座のストリーミングデータをダウンロードする
    for url_kouza_size in url_kouza_sizes:
        #URL/講座名(=MP3タグ名)/ファイルサイズを格納する
        url=url_kouza_size[0]
        kouza=url_kouza_size[1]
        size=url_kouza_size[2]
        # print(f"url:{url} / kouza:{kouza} / size:{size}")

        # listdataflv.xmlのコンテンツを読み出す
        listdataflv_xml = download_dir+"/listdataflv.xml"
        urllib.request.urlretrieve(url, listdataflv_xml)
        xml_tree = ET.parse(listdataflv_xml)
        os.remove(listdataflv_xml)

        # 各LessonのストリーミングデータをMP3に変換してダウンロードする
        for xml_element_music in xml_tree.findall("music"):

            # 放送年月日と年度を取得する
            month=int(re.findall(r'\d+', xml_element_music.get("hdate"))[0])
            day=int(re.findall(r'\d+', xml_element_music.get("hdate"))[1])
            nendo=int(xml_element_music.get("nendo"))
            year=datetime.date.today().year

            if (12==month):
                # 放送日が12月の場合は放送日の年を年度年に設定する
                year=nendo
            # print(f"nendo:{nendo} / year:{year} / month:{month} / day:{day}")

            #※ボキャブライダー対応
            #放送年度年とダウンロード年の差が2年以上ある場合はダウンロードしない
            if ( datetime.date.today().year - nendo ) > 1:
                continue
            #放送年度年とダウンロード年の差が1年で放送月が4-11月の場合は一ヶ月以上前のコンテンツと判断してダウンロードしない
            elif ( ( datetime.date.today().year - nendo ) == 1 ) and ( 3 < month ) and ( month < 12 ):
                continue
            else:
                #放送日とダウンロード日が1週間以上離れている、もしくはダウンロード日以降に放送予定のものはダウンロードしない
                elapsed_day = ( datetime.date.today() - datetime.date(year,month,day) ).days
                if ( elapsed_day < 0 ) and ( 7 < elapsed_day ):
                    continue

            # MP3に埋め込むタグ情報をセットする
            tag_title=kouza+" "+"{0}年{1}月{2}日放送分".format(year,str(month).zfill(2),str(day).zfill(2))
            tag_year=nendo
            tag_album=kouza+"["+str(nendo)+"年度]"
            # print(f"tag_title:{tag_title} / tag_year:{tag_year} / tag_album:{tag_album}")

            # MP3のダウンロードパスをセットする
            download_subdir=download_dir+path_delimiter+kouza+"["+str(nendo)+"年度]"
            os.makedirs(download_subdir, exist_ok=True)
            download_filename=kouza+" "+"{0}年{1}月{2}日放送分".format(year,str(month).zfill(2),str(day).zfill(2))+".mp3"
            download_path=download_subdir+path_delimiter+download_filename
            # print(f"download_path:{download_path}")
            # ストリーミングファイルのURLをセットする
            download_url="https://vod-stream.nhk.jp/gogaku-stream/"
            if len(xml_element_music.get("dir"))>0:
                download_url+=xml_element_music.get("dir")+"/"
            download_url+=os.path.splitext(os.path.basename(xml_element_music.get("file")))[0]+"/index.m3u8"
            # print(f"download_url:{download_url}")

            # ffmpegのダウンロード処理用コマンドラインを生成する
            command_line=f"{ffmpeg_bin}" \
                         f" -http_seekable 0" \
                         f" -i {download_url}" \
                         f" -id3v2_version 3" \
                         f" -metadata artist=\"NHK\" -metadata title=\"{tag_title}\"" \
                         f" -metadata album=\"{tag_album}\" -metadata date=\"{tag_year}\"" \
                         f" -ar 44100 -ab 64k -c:a mp3" \
                         f" \"{download_path}\""
            print(command_line)

            # ダウンロード処理を実行する
            if( os.path.isfile(download_path)):
                # すでにダウンロード済みファイルがある場合
                if( os.path.getsize(download_path)<=size ):
                    #ファイルサイズが想定サイズに満たないときはダウンロード処理を行う
                    os.remove(download_path)
                    subprocess.run(command_line,shell=True)
            else:
                # ダウンロード済みファイルがない場合
                #  -> ダウンロード処理を行う
                subprocess.run(command_line,shell=True)

if __name__ == "__main__":
    main()

上記のスクリプトは、ダウンロード処理の自動化の他に以下の処理を追加しています。

  • 実行環境でパラメータを自動で変更する処理を追加
  • NHKラジオ英会話以外の語学講座も連続してダウンロードできるように機能を追加
  • xmlのdir属性に応じてURLを変更する処理を追加
  • 放送日の’年度’の記述を暦年で表記できるように処理を追加
  • mp3のalbum,title,yearタグに講座情報を追加
  • ファイル名・タグ名の月日の表示を0でパディングするように修正
  • WindowsのExplolerでmp3の再生時間が正しく表示されない問題に対処するため、ffmpegのエンコードオプションに’-ar 44100 -ab 64k’を明示的に追加
  • ダウンロード済みのファイルが存在する場合、ダウンロードが不完全かどうかをファイルサイズでチェックして、サイズが足りない場合は再ダウンロードする処理を追加
  • MacとWindows上でダウンロードする際に、ダウンロードフォルダ名に週番号を付与する処理を追加。
    ※PC上でのダウンロードはNASの自動処理がトラブルを起こしている場合の代替手段になります。ダウンロード後にNASにファイルを移動しますが、週ごとに正しくダウンロードできているかどうかを確認する必要が出てくることと、複数週分のファイルが一つのフォルダにまとまってしまうと確認と移動が大変になります。そこで、ダウンロードフォルダに週番号をつけてファイルが混ざらないようにしています。

スクリプトはリスト’url_kouza_sizes’にセットした講座情報を一つずつ上から下へ処理していくだけなので、プログラミング経験があれば処理内容は理解できると思います。

mp3のタグ名やダウンロード時のフォルダ構成やファイル名など、それぞれの要望に合わせて細かな調整もスクリプトの当該部分を変更するだけで、簡単に対応できます。

ダウンロードURLのフォーマットが変わったりlistdataflv.xmlのパスが年度ごとに変わったりすることがありますが、その場合はCaptureStream2のソースコードNHK.htaのコメントなどからURLを探し当ててスクリプトを変更できさえすれば、ツールのアップデートを待たずにダウンロードできるようになります。

ファイルがダウンロード済みの場合はダウンロードをスキップするようにしているので、何度実行してもダウンロードは一回のみになります。この仕様により、毎日定時に自動実行するようにすれば、ネットワークトラブルなどでダウンロードに失敗した場合でも週内はリトライがかかるため、ダウンロード漏れの可能性を減らすことができます。

Synology NASで自動ダウンロードする

Synology NASは単体でpythonとffmpegを実行することができるので、NAS単体で上記のスクリプトを動作させることができます。

また、SynologyのNASはスケジュール実行が簡単に設定できるので、定期的なダウンロード処理をする手段として使わない手はありません。

しかし、Synologyにデフォルトでインストールされているffmpegのバージョンは4.1.3と大変古く、上記の方法によるダウンロードができません。このため、有志が配布しているffmpegを下記手順でインストールする必要があります。(2022年11月16日現在では4.4.2がインストールされます)

Fig. パッケージセンターのコミュニティに http://packages.synocommunity.com/ を追加
Fig. ffmpegを検索してインストール

なお、このときにインストールされるffmpegのパスは /volume1/@appstore/ffmpeg/bin/ffmpeg になり旧版が上書きされるわけではないので、パスを指定せずにffmpegを呼び出してもここでインストールしたffmpegは起動しません。このため、pythonスクリプトから新たにインストールしたffmpegを起動する場合は絶対パスで指定する必要があります。

ffmpegのデフォルトパスを変更する方法もありますが、スクリプト内で絶対パスで指定するほうがNASシステムへの影響がなく、システムアップデート時に修正が上書きされて修正を元に戻されて動作しなくなるトラブルもないので安全です。

Pythonはパッケージセンターのものを指定してインストールすればいいです。バージョンは3.9以上であれば問題はないと思います。

あとはコントロールパネルにあるタスクスケジューラーで定期実行するように設定すれば毎週自動的にダウンロードされるようになります。

正しくダウンロードされたかをチェックする

自動ダウンロード処理が実行されても、自宅のネットワークトラブルやXML仕様の変更など、様々な要因でダウンロードに失敗する可能性があります。

ダウンロード対象の番組が1−2本程度で、毎週聞いているのであれば週内にダウンロード失敗に気づき再ダウンロードもできますが、多数の番組をダウンロードしているとチェックの手間も無視できません。

番組によらずファイルサイズは再生時間に比例するので、Everythingを使うなどして最新のダウンロードファイルのファイルサイズを一覧できれば、ファイルサイズがすべて同じかどうかを確認するだけで大丈夫です。ただし、確認のたびにPCを起動しNAS上のフォルダを開いてファイルサイズを確認するというのは手順が多く、毎週定期的に行う作業手順としては少々煩雑です。

私の場合はNASに付属している音楽再生ソフト(Synologyの場合はAudioStation)のスマートプレイリスト(条件にあった曲を自動で検索してリストアップ)を利用して、スマホとPCのブラウザ上でダウンロードチェックできるようにしています。

リスト表示の条件は以下のように設定しています。

SynologyのAudioStationにはファイルサイズを表示する機能はないので、ファイル再生時間が全て15分になっているかどうかでダウンロードが正しく行われているかを確認しています。

SynologyのAudioStationはスマホアプリもあり、このプレイリストを表示してダウンロードチェックをしたあと、このプレイリスト上でファイルをスマホにダウンロードする手順でダウンロードチェックをしてます。

参考になれば幸いです。

ワイヤレス充電スタンドをDIY

ワイヤレス充電対応スマホは、日々の充電で置くだけですむという手軽さの他に、USBケーブルの挿抜が不要になるのでスマホのコネクタの故障を避けられるのも良いです。

ところがワイヤレス充電は、充電のスイートスポットに確実に重ねる必要があることから、置くたびに充電されているかどうかを確認する手間が増えました。

さらに不可抗力でスマホと充電台がずれたりしても充電されなくなるため、USB充電と比較して充電の確実性が低くなり、逆にストレスが溜まるようになりました。

これを解決するにはスタンド型を買うのが一番いいですが、お値段はそこそこする上にスマホによってスイートスポットが変わるため、汎用性がないところに不満があります。

そこで100均のもので充電台を作成したところ、700円弱で作成できました。

材料は2個で100円のミニブックエンド、ワイヤレス充電パッド(なんでも良い)、L字充電ケーブル、両面テープのみです。

作り方はブックエンドを少し折り曲げてスマホが乗るようにした後、ワイヤレス充電バッドを両面テープで貼るだけです。見た目が気になる場合は装飾テープなどでデザインすればOK。

ワイヤレス充電パッドは100均で500円程度で売ってます。ただ、スタンド型にするとワイヤレス充電パッドが部屋で悪目立ちするので、889円と少し値段が高くなりますが写真で使用しているIKEAのLIVBOJ リヴボイが、デザインが真っ白(or真っ黒)でシンプルな丸型、かつLEDが無く充電中も落ち着いて動作しているのでおすすめです。

なお、過充電対策で常に満充電状態を避ける場合は横置きにして置くだけで充電されない状態になるので、充電したくない場合にもこのスタンドが使えるというおまけも付きました。

参考になれば幸いです。

TeamsでShokzのマイクミュート時の警告音を止める

※2023/5/10現在、Windows 10(21H2)/Teams 3.3.0.0の組み合わせでOpenRun Proで接続中にマイクミュートをしてもビープ音が鳴らなくなっていることを確認しています。私の環境のみで鳴らなくなったかどうかは不明であるのと、Teamsのアップデートで問題が再発することも考えられるので記事は引き続き掲載しておきます。

在宅勤務がデフォルトになったことに伴い、打ち合わせや発表会などはTeamsで行うことが多くなりました。

Teamsでミーテングの時は耳を塞がずに通話が可能なShokzのOpenRun Proを使用しており、会議中でも他の人と直接話ができるため大変重宝しています。

ところが、TeamsでShokz使用中にミュートすると10秒毎にミュート状態を警告するピープ音が定期的に鳴ります。1対1での通話ではミュートすることはないので問題はないですが、ミーティング中はミュートすることが多いため、この音はかなり鬱陶しいです。

この音はPowerToysのConference Mute機能でマイクをミュートにしても発生するため、Teamsアプリの問題ではなくWindowsとShokzの組み合わせで発生する問題だということがわかります。

原因を調べてみると、Windows側でミュートするとミュートされたことがデバイスドライバを通してShokzに通知され、Shokzが警告音を発するようです。根本的な対策としてはShokz側でこの音を停止することですが、Shokzアプリには設定がないことから、Windows側で対応する以外に方法はなさそうです。

ネットを調べてみると仮想オーディオドライバを入れると解消できるとありました。原理を調べたところ仮想オーディオドライバがミュート信号を受信し、Shokzにはミュート情報が伝わらなくなるために警告音が鳴らなくなるようです。

ネットではVB-CABLE Driverを使用した設定例が多かったので、同様にVB-CABLE Driverを利用したところ、Teamsでの通話には全く影響がないことも確認できました。レジストリなどの編集する必要がなくシステムを破壊する可能性も低いことから、この方法が最も良さそうです。

ただし、ネット上の情報で設定しようとすると、設定と確認方法の手順が多くややこしいので、簡略化した設定手順を画像付きでメモします。画面と項目名は英語版になります。

設定手順

VB-CABLE Driverをインストールする

VB-CABLE Driverをダウンロードしてインストールする。(要管理者権限)
https://vb-audio.com/Cable/

Shokzのマイク入力をVB-CABLE経由に変更する

  1. Windowsの通知領域のサウンドアイコンを右クリックしてサブメニューを表示する
  2. メニューから’Sounds’をクリックする。すると’Sounds’コントロールパネルが表示される(‘Sounds’コントロールパネルが表示できれば手順は問わない)
  3. ’Recording’タブを選択する
  4. ‘OpenPro shokz’をダブルクリックする。すると’Headset Property’ウィンドウが表示される
  5. ‘Listen’タブを選択し、’Playback through this device’に’VB-Audio Virtual Cable’を選択する
  6. ’OK’ボタンを押して変更を確定させた後にShokzから発話するとOpenPro ShokzとVB-Audioの両方のボリュームメータが反応することを確認する

Teamsのマイク入力をVB-CABLEに変更する

Teamsの’Device’で音声入力を’VB-Audio Virtual Cable’に設定する。

参考になれば幸いです。

100均で手に入る材料でMagic KeyboardとMagic TrackPadの位置を固定する

下記のようなMagic Keyboard/Magic TrackPadの位置を固定する台を100均で手に入る材料で満足なものができたので、再作成する際に同じものが作れるようにメモを残しておきます。

製作のきっかけ

MacBook Air M2を家で使うときには、DellのU4021QW(40インチ5Kワイドディスプレイ)に接続して使用しています。このとき、MacBook Airのキーボードを使おうとすると下記のようにMacBook Airのディスプレイが邪魔になります。

そこでMacBookはクラムシェルモードで使用し、外付けキーボードとマウスで操作してみましたが、MacBookのTouch IDが使えなくなることでスリープから復帰する際にいちいちパスワードを入力しなければならず、かなりストレスを感じるようになりました。Touch IDの便利さは捨て難いためMagic Keyboardを購入しました。

さらにMacBookのTrackpadの操作性は私にはマウスより便利なので、Magic Trackpadも合わせて購入しました。

しかし、Macbookとは違いキーボードとトラックパッドは一体化していないため、キーボードを移動させるとトラックパッドも一緒に移動させて位置関係もその都度調整しなければならず、マウス操作の時にはなかったストレスを感じるようになりました。

加えてMagic TrackPadはMacBookとは違い下記のように傾斜がついているため、Magic KeyboardとMagic TrackPadを上下に配置するとキーボード操作中に手のひらがトラックパッドに触れてカーソルが飛ぶことが多々あり、かなりのストレスになります。

Magic KeyboardとMagic TrackPadを横に並べれば解決できますが、手の動きがマウスを使っているのと大して変わらず操作もしにくくなり、Magic TrackPadを使う意味が薄れてしまいます。

そこで解決策はないかとWebで調べたところ、下記のeXpress keyboard platformという製品が一つの解決策になってくれそうでした。しかし、長く文章を打つ場合にはHHKBも使用したいという要望もありMagicKeybord以外のキーボードが使えないことがネックになること、価格が$159+送料+税金で軽く2万円を超えることで購入は躊躇してしまいます。

これと同じようなものを自作されている方もいましたが、下記の私の要求を全て満たしてくれるものではありませんでした。

  • 近所の店で簡単に手に入るもので作れること
  • 家にある道具のみで作成できること
  • 安いこと
  • mm単位未満の切削精度を求めずに作れること
  • 処分しやすいこと
  • 机の上で滑るように移動させられること
  • 自分の使用環境に合うように細かく調整できること

そこで私の要求を満足すべく台を試作した結果、100均で手に入る材料のみで上記の要求を満たせるMagic keyboardとMagic Track Padを固定する台ができました。

作り方

材料

基本的に全て100均(CANDo !)で手に入れられるものばかりですが、用途にさえ合っていれば違う材料を使用しても問題はないです。

  • 板材
    ※固定台のベース。大きさは450mmx150mm程度で厚みは9mm必須。材質はなんでもよい
  • コルクシート
    ※パームレスト部分の厚み調整用。3mm厚でA5サイズ程度あればOK。3mm厚の板材でも可
  • 両面テープ
    ※板、コルク、クリアファイルを接着する
  • A4サイズのクリアファイル1枚
    ※台の下に敷いて、テーブル上で滑らせて移動できるようにする
  • プラ板
    ※TouchPadのミスタッチガード用。281x115mmの大きさ(A4サイズ弱)が切り出せる位の下敷き程度に薄く硬いものがいい(クリアファイルでも問題はなさそうです)
  • スポンジゴム
    ※キーボードに傾斜をつける
  • セロテープ
    ※板材を接合する

製作方法

  1. 板を下記のサイズで切断する。
    ※板の接合部分とMagic TrackPadが接する部分は正確に直線と直角を出しておく必要があります。ただ手引きでノコを入れると歪むので、青線部分の板の端の直線と直角を利用しましょう。
  1. クリアファイルの下記部分を切断して広げ、245×281(mm)の大きさに切り出す。
    ※クリアファイルの折り目の部分が①の青線部分に当たるように切断すると綺麗な四角に切断できます。
  1. 1で切り出した板を下記の形でセロテープで接合する。
    その後、板の裏に両面テープを貼り付け、2で切り出したクリアファイルの上に貼り付ける。
    接合の際にはTrackPadを入れて位置を合わせ、後で位置などを調整できるように各接着面積は最小限にするといいです
    ※青線部分は板切断時に残した直線部分を当てて正確に直角・直線を維持するようにしましょう
  1. ②と③のサイズにコルクシートを切り出し、板に貼り付ける。
  2. キーボードを①に乗せて、下記部分が埋まるサイズのコルクシートを切り出し、板に貼り付ける。
    ※ここでトラックパッドとキーボードの位置関係を自分の好みにあうように調節しましょう
  1. トラックパッド充電時にトラックパッドを取りやすくするため、下記位置に切れ込みを入れる
  1. トラックパッドを使用しながら充電できるようにするため、Lightningケーブル差し込み口部分に切り欠きを入れる
  1. プラ板を下記のように切断し、両面テープでコルクの上から貼り付ける。
    ※このプラ板はキーボード入力時のMagic TrackPadのミスタッチを防ぐためのものになります。
  1. (キーボードに傾斜をつけたい場合)板上部にスポンジゴムを両面テープで貼り付け、キーボードを乗せて出来上がり。

後は使用しながら、最適なキーボードの傾斜、キーボードとTrackPadの位置関係、Trackpadのミスタッチガードの形状、各部品の材料などを模索し、最終的な設計が確定したら、自分好みに再作成すればいいです。

私の場合は上記設計が一番Fitしており、HHKBで作業をしたい場合は下記のようにキーボードを載せ替えて作業できるようになり、さらにはテンキー付きのキーボードなど、Magic Keyboard以外でレイアウトを固定したい場合には、板とクリアファイルのカットサイズを変えるだけで対応もできます。

製作コストが安くて、板さえ切り出してしまえば、製作と調整にそれほど手間はかからず、切削精度もそれほど要求せず、入手が容易で安くやり直しもしやすいという点で、製作しやすいと思います。

参考になれば幸いです。