のらぬこの日常を描く

ノージャンルのお役立ち情報やアニメとゲームの話、ソフトウェア開発に関する話などを中心としたブログです。

使ってみた!Bluetoothでノイズキャンセリングな高性能ヘッドホン・イヤホン

どうも、のらぬこです。

以前、ニンテンドースイッチなどのbluetoothに対応していない機器を、Bluetooth接続対応のイヤホン等と、無線での接続を可能にする「Bluetoothオーディオトランスミッター」と呼ばれる製品の紹介記事を公開しました。

noranuk0.hatenablog.com

こちらの記事、大好評とはいかないまでも、比較的多くの方にお読みいただいたようで、僕もうれしい限りです。

さて、Bluetoothオーディオトランスミッターを使うには、当然ながらBluetooth対応のヘッドホン、イヤホン、スピーカーなどが必要になってきます。でも、前記事では、そちらについては全く触れていませんでした。

今回は、その辺の話を補ってみようという意味合いも込めて、僕が持っているBluetoothヘッドホン、イヤホンを紹介したいと思います。

f:id:noranuk0:20171112151705j:plain

イヤホン、ヘッドホンを使うシチュエーションは?

僕がヘッドホンを使うシチュエーション、利用シーンはだいたい下記のような感じです。家で使うことは滅多になく(夜遅くゲームするときに使うことが稀にあるくらい)、利用シーンは基本外出時です。

  • 利用シーン

    • 通勤中の電車内(割と轟音きつめな地下鉄区間も含む)
    • ひとりでの外食中や休憩中。
      • ファミレス、ファーストフード店などで、お昼ご飯や夜ご飯を食べてるときです
  • 利用用途

    • 音楽(ジャンルは、アニメ系、ゲームサントラ系などを中心に色々)鑑賞
    • ゲーム機と接続して(3DS/PSVita/Nintendo Switch)
    • 動画(主にアニメの録画番組)視聴

と、そこそこの騒音の中で様々な用途で使っている感じです。

今回紹介するヘッドホン、イヤホンは?

今回紹介するヘッドホン、イヤホンは、僕が持っている以下の5機種になります。

  • MDR-ZX750BN
  • MDR-ZX770BN
  • MDR-EX31BN
  • MDR-1000X
  • h.ear in Wireless(MDR-EX750BT)

オーディオ機器メーカーとしてはソニーが特別好きというわけでもないのですが、気が付いたらソニー製品ばっかりになってしまいました(笑) 基本的に外出時(しかも、現在利用中の地下鉄、地下鉄の中でもたぶんかなり煩いです)の利用メインなので、1機種を除いてすべてノイズキャンセリング機能搭載となっています。

一応、ほかにもSHUREの「SE215 Special Edition」というイヤホンを持っていますが、Bluetoothの便利さに慣れてしまい、現在はお部屋の隅っこで眠っております。

一つずつ紹介

取り敢えず手持ちのイヤホン、ヘッドホンについて、良いところ、おすすめポイントなどを交えながら一つづつ紹介してみたいと思います。

MDR-ZX750BN

  • 対応コーデック
    • SBC/aptX
  • 有線接続
    • 可能
  • NC性能
    • ファミレス、ちょっとザワザワした喫茶店、地上を走る電車ならほぼ問題ないレベル、地下鉄はきつい

はじめて買ったBluetoothノイズキャンセリング全部入りのヘッドホンです。比較的手の出しやすい価格で、Bluetooth+ノイズキャンセリングの両方がついたヘッドホンが欲しい!というお話でしたら、こちらのヘッドホンが一番のお薦めだと思います。

音質もまずまずで、低域から高域までちゃんと鳴らしてくれるし、こもった音でぼやーーっとした感じっという事もありません。

もちろん、同価格帯の有線ヘッドホンには流石に太刀打ちできませんが、機能を考えれば優秀なヘッドホンだと思います。

ただしこのヘッドホンの最大の問題は、すでに生産終了となっており、中古でしか出回っていないことです。中古であることがあまり気にならないのであれば、価格もそれほど高くないですし、今でも十分にお薦めできると思っています。

MDR-ZX770BN

  • 対応コーデック
    • SBC/aptX
  • 有線接続
    • 可能(3.5インチミニジャックの接続口の部分が少しくぼんでおり、付属ケーブル以外では奥までしっかりと刺さらないかもしれません)
  • NC性能
    • ファミレス、ちょっとザワザワした喫茶店、地上を走る電車なら十分な性能、地下鉄はややきつい。「MDR-ZX750BN」よりも性能はよいと思います。

型番的にも価格的にも、最初に紹介した「MDR-ZX750BN」をちょっとグレードアップさせた感じの後継品。ただ、個人的な主観になりますが、音質の観点では1〜2段階グレードダウンした廉価版的な印象でした。

特に顕著なのが高域のかすれです。 シンバルなどの高域楽器はシャリシャリしたノイズがのっかっているような音になってしまっています。

また、「MDR-ZX750BN」と比べて音が籠もった感じになっています。

全体的な音のバランスはよいのですが、高域のかすれに関しては特に意識しなくても普通に気づくレベルです。

ただし、ノイズキャンセリングの性能は前機種からそこそこ向上しています。音質よりもノイズキャンセリングの方に重点を置いた機種なのかも知れません。

これらの2機種で検討するのであれば、中古全然ありですっていう方であれば「MDR-ZX750BN」をおすすめしたいです。

「MDR-ZX770BN」は、音質よりもノイズキャンセリング性能重視で、高級機種を買うほどの予算はなけど(それでも1万円くらいはかかってしまうのですが)性能のよいノイズキャンセリングBluetooth接続のヘッドホンがほしいっていう方には向いているかも知れません。

MDR-EX31BN

  • 対応コーデック
    • SBC/aptX
  • 有線接続
    • 不可能(Bluetooth接続専用になります。充電が切れた場合は音を聞くことができなくなります)
  • NC性能
    • あるのとないのでは多少は違う程度。そんなに煩くない場所では効果は感じられますが、電車内などそれなりにうるさい場所ではあまり役に立たない気がします。

こちらの商品のみ中古で購入しました。必要性は全く無かったのですが、値段が安かったので衝動買いしてしまった感じです。

付属しているイヤホンの音質は、新品価格で考えると割と微妙ですが、中古5000円未満というお値段ならまずまずと思います。

こちらのイヤホンは、Bluetooth受信部とイヤホンを、3.5インチミニジャックで接続して使う構造になっています。

したがって、標準で付いているイヤホン以外に、普通のイヤホンもつなぐことが出来るようになっています(もちろん、ノイズキャンセリングは使えなくなります)。

なので、好きなイヤホンを刺して Bluetoothオーディオトランスミッター的な使い方をすることも可能です。

MDR-1000X

  • 対応コーデック
    • SBC/aptX/LDAC
  • 有線接続
  • NC性能
    • ファミレス、ちょっとザワザワした喫茶店、地上を走る電車では非常に快適です。さらに地下鉄等騒音がちょっと強めの環境でも申し分ない性能を発揮してくれます。ただ、地下鉄の中でもうるさい(と思う)京王線では、流石にちょっとボリューム盛らないと、動画音声などは聞きづらいこともあります。

こちらが僕が現在メインで使用しているヘッドホンになります。 というか、充電し忘れて使えない時以外は、基本これしか使ってませんw

性能については、値段が高いだけあって、音質、ノイズキャンセリング性能共に、他の機種を圧巻しています。

まず音質についてですが、低域から高域までアレンジされた感じもなくバランスよく比較的クリアに鳴らしてくれます。さらにノイズキャンセリング性能も抜群で、通常利用で不満に感じることはほぼないと思います。

BluetoothコーデックとしてSBC、aptXに加えて、「LDAC」というソニー独自規格のBluetoothコーデックが搭載されています。Xperiaシリーズなどの対応機器と接続すれば、ハイレゾ相当の高音質なサウンド再生も可能になっています。

一つ難点を挙げるとすれば、耳をすっぽり覆う形のヘッドホンなので、夏は蒸れることとくらいです。

予算があれば是非おすすめしたいヘッドホンです。

なお、2017年10月に発売された後継モデル(WH-1000XM2)では、ノイズキャンセリングの機能、性能が更に良くなっているようです。また、対応コーデックに「aptX HD」が追加されています。価格は少し上がってしまいますが、これから購入される方は、こちらも候補に入れてよいかと思います。

h.ear in Wireless(MDR-EX750BT)

  • 対応コーデック
    • SBC/aptX/LDAC
  • 有線接続
    • 可能(付属の専用ケーブル必須)
  • NC性能
    • NC非搭載

MDR 1000Xの充電を忘れてた時や、荷物が多くてヘッドホンは持ち歩きたくない時などに使う予備機という位置づけで、カバンには常に入っています。

上で紹介した機種と比べると、くっきりとした若干高域寄りの音作りがされています。もちろん安いヘッドホンにありがちな音ではなく、クリアで聞きやすい音質になっています。

さらに、とても軽量で、首に掛けたときに邪魔になることもありません。

ただし、ノイズキャンセリング機能は積んでおらず、遮音性も特にないため、周囲がそこそこうるさい環境でじっくり聴くようなシチュエーションには不向きかもしれません(普通のイヤホンと同じです)。

ちなみに、こちらもイヤホンもLDAC対応なので、MDR-1000Xと同様に、Xperia等の対応機器では、より高音質なサウンド再生も可能になっています。

ちなみに、2017年の10月に、ノイズキャンセリング搭載した後継的な位置づけのモデル「WI-1000X」も発売されました。ノイズキャンセリング機能付き、ハイレゾ対応ワイヤレスイヤホンという条件であれば、間違いなくおすすめ出来る機種だと思うのですが、価格が高いのが難点です。

今後、新しいのを買うとしたら?

ソニーばっかりで恐縮ですが、「WI-1000X」は結構惹かれております。コンパクトでかさばることもなく、カタログスペックを見る限りでは、ノイズキャンセリングも音質もかなり頑張っているように感じました。

もし、MDR-1000Xが壊れてしまったら、今のところは1番の購入候補になると思います。

ということで、今回は、僕が使ったことがあるワイヤレスイヤホン、ヘッドホンを幾つか紹介させていただきました。

この記事がどなたかのご参考になれば幸いです。

amazon echo plus / google home miniのレビュー記事を書きました。 割と辛口ですがご興味ある方いらっしゃいましたらこちらもどうぞってことで。 noranuk0.hatenablog.com

最後までお読みいただいてありがとうございました。

スマホ落下を未然に防ぐ。安くて丈夫、紐が切れないお薦めストラップ

どうものらぬこです。

最近は、携帯電話、スマートフォンにストラップをつけている方も、林檎端末勢力に押され*1、見かけることも減ってきた気がします。

ですが、ガラケー、アンドロイド端末使いの方に限れば、アクセサリーとして、手が滑って端末を落としそうになったときの保険として、ストラップをつけている方も結構いらっしゃるんではないかと思っております。

さて、僕もガラケー使ってた頃、多分15年とか20年前くらいからずっとストラップ使ってますが、これ、携帯に括り付ける紐の部分が極めて脆く、すぐ切れちゃうんですよね。。。

安いものでも500円くらいはしましたし、買って数週間で紐が切れて使えなくなってしまったりするととても残念な気持ちになってしまいました。

僕と同じように、紐が切れない、長持ちするストラップを探している方もいらっしゃるんじゃないかと思い、今回は、ストラップを「手が滑って端末を落としそうになったときの保険」として使っている方向けに、僕が10年くらい使い続けている、とても頑丈なストラップを紹介していきます。

それがこちらです。

Wiiリモコン専用 ストラップ (4色セット)

Wiiリモコン専用 ストラップ (4色セット)

機種変のときや、流石に薄汚れてきたと感じてきた時に交換はしていますが、それでも3年位は余裕で持つと思います。

こちら、かつて任天堂から発売されていたゲーム機「Wii」のコントローラ用のストラップなのですが、この紐がとても頑丈にできているのです。一応、Wiiのアクセサリーということになってはいますが、もちろんスマホでも使えます。

価格も1本100円〜200円程度で売られています。

ちなみにこのストラップ、Wiiの発売と同時に販売開始されたものなのですが、実は最初から丈夫だったわけではありません。

Wiiの発売後間もなく、海外で、Wiiリモコン(コントローラ)を振るような操作で遊んでいたところ、手が滑ってWiiリモコンが手から離れたと同時にストラップ紐が切れ、Wiiリモコンがテレビに激突しテレビが破損した、という事故がありました。 それを受け、任天堂が「多少乱暴に扱った程度では紐が切れない改良版」を用意した、という経緯があります。

japanese.engadget.com

つまり、現在出回っているWiiストラップは、「ストラップを手にはめた状態では多少振り回した程度では壊れない」程度の強度を持っている、と思っています。

ちなみに、紐の部分はこんな感じです。かなり太いです。 f:id:noranuk0:20170812112205j:plain

xperia Z5にとりつけてみたところです。ストラップの色が割りと派手なので、スマホやケースの色によっては合わせづらい可能性があるのが難点といえば難点。 f:id:noranuk0:20170812112259j:plain

丈夫でコスパもとても良いのですが、3つほど注意点があります。

まず1つ目は、ストラップが丈夫だから多少乱暴に扱っても平気というわけでは勿論ないということです。例えば、ストラップを腕に巻いてスマホを振り回したりすれば危ないのは勿論ですが、ストラップ紐が丈夫なため、スマホのストラップホールが壊れてしまうかもしれません。

2つ目は、ストラップ紐が太いのでストラップホールに通すのに結構苦労します。スマホのストラップホールサイズにも夜と思いますが、途中までねじ込んだ後、爪楊枝などを引っ掛けて紐を引っ張り出す、等のテクが必要なるかもしれません。

そして3つ目は、Wiiは既に過去の製品ということです。 このストラップもいつ市場から消え去ってしまっても不思議ではありません。かなり前から量販店で見かけることもなくなってしまいました。

値段も高くはないですし、丈夫なストラップがほしいなー、と思っていた方、お試しでご購入されてみても良いかと思います。

Wiiリモコン専用 ストラップ (4色セット)

Wiiリモコン専用 ストラップ (4色セット)

というわけで、今回の記事は以上となります。

この記事が、どなたかのお役に立っていただければ幸いです。

*1:極々一部の端末を覗いてiOS系端末にはストラップホールがないので

この怨み、地獄へ流します・・・ ~ iOSアプリ審査提出前のバリデーションフェーズで起きた悲劇を語る

どうも、のらぬこです。

完成したiOSバイス向けのアプリをAppStoreで配信するには、林檎神の理不尽な審査を通過しなければならない。

xcodeで成果物を提出するための操作を行うと、まずはプロジェクト関連ファイルのバリデーションが実行される。

この段階で、例えば「アイコンのサイズが規定のサイズでキチンと作られているか」とか「そもそもちゃんとビルド通るか」等が確認される。

今回は、このプロジェクトコード一式のバリデーションで発生したエラーではまった話をしようと思う。

f:id:noranuk0:20170712131641p:plain

ITMS-90056

This Bundle ****/TabPageViewController.framework is invalid Thr Info.plist file is missing the required key: CFBundleVersion

内部で利用している「TabPageViewController」というフレームワークのInfo.plistがおかしい?必須項目であるCFBundleVersionというキーが存在しないというエラーのようだ。

軽く調べてみたが結局原因は不明のまま。TabPageViewControllerの取り込み方法をcarthageからcocoaPodに変更することで回避できた。

ITMS-90328

Your package contains a file Main.storyboadc/TabPageView.nib with a name that contains invalid characters. Avoid using control characters in the file names.

なんとなく翻訳してみると、「ファイル名に使っちゃいけない文字を使っている」と書かれているような気がする。だが、対象のファイルは “TabPageView.nib” と、使っちゃいけない文字とか全くなさげ。

解決編

原因と修正方法を先に書いてしまう。

まず原因の方だが、このエラーは、ストーリーボードに配置されたViewContorollerのstoryboardIDに変な文字(改行とかタブとかファイル名としてあまりよろしくない文字)が紛れ込んでいた場合に表示される。

ちなみに、そのIDがプログラムから全く参照されていない場合、リリース用にcode validationを掛けるまで「警告」「エラー」等は何も表示されないし、シミュレータや実機などで開発版を動かす際も特に不具合は発生しない。

したがって、原因や修正方法に関する知見を持たないままプロジェクトが最終局面を迎えてしまうと、最後の最後になって初めてこのエラーを目にすることとなる*1

さて、次は修正方法だが、まずはプロジェクトに登録された全てのstoryboardファイル内を「」で検索してみるとよい。

もし、見つからなかった場合は、このブログ記事の内容を読んでも解決策は見つからないと思うので、早々に立ち去って他の記事を探すことをオススメする。

見つかった場合、見つかった箇所周辺のxmlタグを眺めつつ、対象のViewControllerなどを特定し、storyBoardIdなどに特殊文字が紛れ込んでいないかを確認してみようか。

ただしここで注意していただきたいのは、xcodeのプロパティービューを見ても特におかしなところは見当たらない(ように見える)かもしれないことだ。

だがしかし

storyboardId欄などにフォーカスし、ctrl+A → ctrl+Cなどで全選択コピーをして、そのまま他のエディターなどに貼り付けてみると、たしかに妙な文字(僕の場合は)が紛れ込んでいるのである。

最も、とりあえず場所が特定できれば後はどうということはない。問題箇所を適切に修正すれば、このエラーは消えるはず。

調査編

修正は一瞬だったが原因の特定にはそれなりに苦労したので、せっかくだから修正完了までの作業プロセスも書いておく。

まずは、nibファイルが何物なのかと思って調べてみた。検索結果を流し読みしただけなのだが、どうやら画面レイアウト系のファイルのようで、アーカイブ時にstoryboardから生成されるファイルらしい。

そういえばstoryboardファイルの中身ってxmlなので、とりあえずテキストとして開いて TabPageView で検索してみるが、対象の文字列は見つからず。

次に、エラーコードやエラーメッセージ等で検索してみるも、xcode再起動してもう一度試してみれば?的なアドバイスはどこかにあった。一応やってみたけど当然ダメ。

とりあえず、TabPageView.nibというファイルを探してみることにした。

といっても、xcodeがビルドする際に中間ファイル的なものをどこに作るのかわからなかったので、以下のコマンドで対象ファイルっぽいものを探してみる。

$ find / --name TabPageView.nib 2>/dev/null 
/private/var/folders/...../Base.lproj/Main.storyboardcTabPageView.nib

発見。

見つかったディレクトリに移動して lsしてみる。

$ ls -la
total 432
-rw-r--r--   1 * * 7 10 13:08 ?TabPageView.nib
drwxr-xr-x  40 * * 7 10 13:08 .
drwxr-xr-x   4 * * 7 10 13:08 ..
...
...

「何かおかしい」

確かに、ファイル名先頭に不穏な文字がくっついている。

$ cat <TAB>
^HTabPageView.nib

H」を文字コードに直すと「0x08」。

「お前、タブ文字か・・・・」

そこで、ふと気づき、Main.storyboardを一旦カラにし、素のViewControllerを一つ配置した状態で再Validationをかけてみる

言うまでもなく、当然起動直後にクラッシュするとは思うが、少なくともValidationは通った。

storyboardをもとに戻し、配置されているViewControllerを1つずつ削除しながら、どのViewControllerを削除したときにValidationが通らなくなるかを確認しつつ、問題となっているViewControllerを特定する。

特定ができれば、あとは対象のViewControllerがxmlのどこに配置されているかを確認し、xmlの対象箇所を凝視して何かおかしそうな場所がないかを確認すればいい。

原因が特定できればあとは修正するだけだ。

上の「解決編」で書かれたことを実践し、問題を修正。その後、再度バリデーションをかければ、少なくとこのエラーは消えているはずだ。

最後に

エラーの原因解りづらいわ、そもそもxcodeがエラー箇所を正しく指摘してくれないわ、「ファイル名にコントロールコードが含まれてるからダメ」とかエラーコードまで定義されてる割にバッグ実行の為のビルドは成功するし、シミュレータ実機デバッグ共に問題なし何だが?

UXのなんたるかについて、Microsoft VisualStudio開発チームあたりから一世紀ほど学んでから出直してきてほしい。

取り敢えず彼らには、不幸の手紙と藁人形と五寸釘10セット程を是非とも寄贈して差し上げたいと思った次第。

ワラ人形(藁人形)セット

ワラ人形(藁人形)セット

今回の記事は以上だ。

*1:僕のように

俺の右腕に眠りし黒竜よ!力を貸してくれっ! ドッカァー!コンポォォーーッズ!!

どうものらぬこです。

某社関係者のどなたかが、類似するエントリー名で記事書いてたら申し訳ないので一応検索してみたのですが、そういうのはなさそうなので、取り敢えずこのフレーズ頂いときます<(__)>*1

というわけで、今回は Docker の話をしようと思います。

www.docker.com

dockerとは

今更な話だし、検索すれば答えもたくさん出てくるだろうけど、取り敢えず僕の理解では「軽量でそこそこセキュアで再利用可能なアプリケーションorデータコンテナ」という認識です。

コンテナは基本的にはLinuxOSベースの仮想環境です。マイクロソフト社に御布施をしてWindows Server 2016を導入すれば、Windowsベースの仮想環境を用意することも可能みたいです。 でもそんなお金ないし、僕は今のところその必要性も感じてないので、触ってみたことはありません。

なお、コンテナはあくまで仮想環境なので、実PCで直接動かすことはできません。Dockerというソフトウェアを使って、ホストOSの上に構築した仮想的なLinux環境として動かします。

僕がDockerを触ってみようと思った訳

現在の職場、メイン言語はPHPです*2

さて、PHPを使われている方の間では常識なのかもしれませんが、このPHPという言語はなかなか曲者で、マイナーバージョンレベルの更新ですら(7.0->7.1等)、上位互換性(バージョンアップしても今まで動いてたコードはちゃんと動くように作ってるよ的な)が割りと確保されていないことも多いという、個人的には割りとFA?って感じの特徴があります。

ローカルで確認したいとき等、一々PHPのバージョン切り替えるのめんどいし、そういえばdockerとか使えばなんか色々はかどるんじゃないかなとなんとなく思ったのが、最近dockerの勉強を始めたきっかけです

dockerの全貌をなんとなく理解してみる

ある概念を新しく勉強するときは、自分の知っている類似の概念に置き換えてイメージしていくと理解がしやすいと考えています。

dokcerもそんな感じで覚えていきました。

  • docker hub

    • githubみたいなもの。汎用的なものから特定用途向けまで、様々なDocker Imageが公開されている。
      • 基本的には、public公開。docker hubにお金を払えばプライベートなものも作れるし、自分で Dokcer hubみたいなのを立てることも可能(プライベートレジストリと呼ぶ)。
    • githubと違うのは、githubでは様々なプラットフォームで動くアプリケーションやらライブラリ等、様々な種類のものが公開されているけど、docker hubで公開されているのは DockerImageと呼ばれるカスタマイズされたLinuxの起動イメージ or それを作るための設定ファイル
  • Docker Image

    • UbuntucentOSなどのLiveCD(DVD)メディア的なもの。docker hubに公開されているものをそのまま使うこともできるし、自分でほぼ一から作ることもできる。また、docker hubで公開されているイメージをさらに自分でカスタマイズすることもできる。
    • LiveCDと違うのは以下の2つ。
      • LiveCDと違って、PCから直接起動することはできない。   * 代わりに、DockerEngineと呼ばれる、DockerImageを動かすための仮想環境を構築するソフトウェア(VMWareとかVirtualPCみたいなやつ)使って、起動中のOSの上に仮想的なLinux環境を新しく立ち上げる(この起動中の環境のことをコンテナっていう)。
      • LiveCDは読み取り専用だけど(HDDとかUSBメモリーをマウントすればもちろんマウント先には書き込み可能)、DockerImageには書き込みもできる。
        • /root だろうが /etc だろうがどこでも書き込み可能。
        • 書き込まれた内容ははDockerImageの中に直接保存されるのではなく、「どのファイルをどういう風に書き換えた」という差分情報のみがDocker Imageとは別の場所に保存されるため、追加、削除、変更内容は元のイメージには影響しない。
          • たとえ「rm -rf /」してしまい絶望に飲み込まれたとしても、元のイメージから新しいコンテナを起動すれば全ての変更は無かったことになっています。
  • Dockerfile

    • 自分好みのDcker Image を作るための設定ファイル
      • 「元となるDockerImage+それに対しての変更箇所」みたいなことを書いていく感じ。
        • 例えば、素のUbuntu16.04イメージにに apache2をapt-getでインストールして、/etc/apache2/httpd.conf をローカル環境のファイルで置き換える みたいなことを書いていく。
  • Docker Container

    • 起動中のDockerImageのことをコンテナって呼びます。
    • 1つのイメージから複数のコンテナを起動することができます。
      • 1つのイメージから複数のコンテナを起動しても、それらはお互いに干渉しません。
        • 例えば、コンテナAで /etc/apache2/httpd.conf を書き換えても、その変更は コンテナBには適用されません。
    • 色々な種類のイメージを用意すれば、色々な種類のコンテナを同時に起動することも勿論できます
    • コンテナのリソース(ファイルシステムだったりネットワークリソースだったり)は基本的に隔離されているので、共有はできない。
      • ただし、起動時にパラメータで指定したり、Dockerfileに設定を記載しておくことで、ネットワークの特定のポートを使用して通信したり、ファイルシステムの特定のパスをホストOS(Docker Containerを動かしているOS)と共有することなどが可能。
  • docker-compose

    • 本エントリーのタイトルにもなった、なんとなくかっこいいコマンド
      • このフレーズは、前職の馬場さん(仮名)によって生み出されたもので、サービスをデプロイするには、このフレーズを馬場さん(仮名)にシャウトしていただかなければならないという暗黙のルールがありました。
        • ただし、「俺の右腕に~」の部分は本人が恥ずかしがって省略されていました
    • 複数の Dockerコンテナが連携して動作するようなシステム(例えば、nginx+PHPが動いてるコンテナとPostgreSQLが動いているコンテナとRedisが動いているコンテナ、など)で、複数コンテナの連携(ネットワークの接続設定やファイルシステムの共有っぽい設定、各コンテナの起動順序など)をいい感じにいろいろやってくれる、確かにすごいコマンド。
    • それぞれのコンテナをどういう風に連携させるのかは docker-compose.yml というファイルに書いておく。
    • 現在勉強中。

dockerの使い方

動作環境やインストール方法、動作原理なんかは検索すれば腐るほど出てくるのでその辺は書きません。

ここでは、使い始めるとかならず使うコマンドを、僕がよく使うコマンドを中心に備忘録的にまとめておきます。

Docker hubからImage 取得

$ docker pull <リポジトリ名:タグ名>
* alpine:latest -> 軽量コンテナ向け
* busybox:latest -> データコンテナ向け

alpine linux + apache な Dockerfile

  • alpine linux は、Docker Image(基本パック)のサイズ僅か6Mb程のとてもコンパクトなlinuxDistribution
  • 起動後のシェルで # apachectl startapacheが起動する
    • # wget http://localhost:80/ で index.html が落ちてくれば成功
FROM alpine:3.6

RUN apk add --no-cache apache2 apache2-utils && \
    mkdir /run/apache2 && \
    chown apache:apache /run/apache2

Dockerfile -> image 作成

$ docker build [-t <イメージにつける名前] .

作成済Imageの一覧を出力

$ docker images

不要になったImage 削除

$ docker rmi <リポジトリ名:タグ名 or イメージID>

Imageをまとめて削除

$ docker images | awk '{print $3}' | xargs docker rmi
* 一部を消したいときは間にgrepなどでフィルタ

作成済Imageからコンテナを作成&起動

オプションがいっぱいありますが、コンテナのTCPポートをホストOS(Dockerを起動しているOS)のTCPポートにマップしたり、ホストOSのファイルシステムパスをコンテナから見えるようにしたりといろいろな設定ができます

$ docker run [-d] [-it] [-p <ホストポート番号>:<コンテナポート番号>] [-v <ホストパス>:<コンテナパス>] [--rm] [--name <コンテナにつける名前>] <イメージ名:タグ名 or イメージID> <起動時に実行するコマンド>

コンテナ一覧

$ docker ps
* 起動中コンテナ

$ docker ps -a
* 停止中コンテナ

コンテナの内部情報を表示

$ docker inspect <コンテナ名 or コンテナID>

コンテナ->イメージ作成

これは、動作中のコンテナのファイルシステムの状態をDocker Imageとして保存するコマンドです。

$ docker commit <コンテナ名 or コンテナID> <イメージ名>

Container 削除

$ docker rm <コンテナ名 or コンテナID>

Container まとめて削除

$ docker ps -a | awk '{print $1}' | xargs docker rm
* 一部を消したいときは間にgrepなどでフィルタ

参照されていないデータボリュームを削除

$ docker volume ls
$ docker volume prune

Dockerの使いどころ

Dockerの使いどころを僕なりに考えてみました。

開発環境として

例えば、Java,PHP,Ruby等の開発言語 + RDB + Redis + ElasticSearch等のシステムをローカル環境で構築するのはちょっと大変ですが、Dockerで nginxのコンテナ、Java(+ApplicationServer)のコンテナ、RDBのコンテナ…等を用意して、それらの関連を docker-compose.yml で管理してあげれば色々楽ができそうです。

チーム開発をしているような場合には、新しいメンバーが入ってきたときも、それぞれのツールの指定されたバージョンをダウンロードして手順書に従ってインストールなんて面倒なことを毎回やらなくても、Docker入れて、そこのdocker-compose.yml使えば環境できるという状態にすることもできるはずと思います。

例えば、PHPのバージョンアップテストをしたい等の目的で、一時的に複数バージョンのPHPを使いたいときなども、なんかうまくやってくれるはず!

開発周辺ツールのホスト環境として

jenkins とか redmine とか selenium + firefox driver の docker imageや docker-compose.yml などは検索すれば簡単に見つかります。

それぞれの環境のために、サーバ用意してOS入れてRubyだのJavaだの入れてRDB入れてREADME.md見ながらconfig設定して〜をやらなくても、簡単に環境を作ることができるはずです。

「コピペ万歳」です!

テストデータがたっぷり詰まったRDBの格納場所として

このエントリー内では特に説明しませんでしたが、Dokcerはアプリケーションコンテナとして使用する方法の他に、データコンテナとして使用することもできます。

マスターデータのみが入ったクリーンな環境や、リグレッションテスト向けに、毎回同じテストデータが入った環境を簡単に作ることができるようになります。

最後に

Dockerをうまく活用できれば確かに色々捗ることも増えそうです。

でも、夢は広がっていますが、この辺を正しく理解して活用出来るようになるには、もう少し勉強が必要のようです。

余談ですが、1冊くらい書籍も読んでおこうかと思って、先日オライリー本を買いました。

安定のオライリー品質で、内容もわかりやすいし翻訳もとても読みやすいです。

Docker

Docker

というわけで、今回の話は以上となります。

お読みいただいてありがとうございました。

*1:しかし、あれだ、このタイトルSEOに全く優しくないな・・・

*2:swiftでiOSアプリ書いたりRubyでbatch書いたりJAVAで技術検証的な目的でテストサイト作ったりもしてます

俺のPHPがこんなに遅いわけがない 〜メソッドキャッシュのお話〜

どうものらぬこです。

最近仕事でPHPを使い始めました。

というわけで、今回はPHPの話でもしようかと思います。

さて、2年ほど前に在籍していた会社で開発していたシステムの話になるのですが、そのシステムが抱えるDBが、規模が巨大だったりだとか、それなりに複雑なクエリーをいくつも投げなきゃいけないとかありまして、パフォーマンスを出すのに割と苦労していたことがありました。

その時は、一緒に仕事をしていた「自称」天才の某中国人様に、『「クラス名+メソッド名+引数の値をつなげたもの」をキーとして、「戻り値の値」をメソッド呼び出しを丸ごとキャッシュとして保持してくれる仕組み』なるものを作ってもらいまして各種諸問題を解決していただいていたのは今では、今では良い思い出です。

ちなみに、当時はJAVAで開発していたのですが、今回、それと同じような仕組みをPHPで作ってみたいと思います。

今回作るもの

クラス内のインスタンスメソッドの呼び出しをフックして、「クラス名+メソッド名+引数の値をつなげたもの」をキーとして、「戻り値の値」をキャッシュする仕組みを作ります。

既存のシステムになるべく簡単に組み込めるものにします。

キャッシュする部分、キャッシュからデータを取得する部分の実装は適当です(Redisなどのミドルウェア使うなりPHPのプロセス内に Hashなどで保持しとくなりご自由にということで)。

お題として、DBに保存された何らかのマスターデータを問い合わせるというシーンを想定して、DBへの問い合わせ部分(IDを渡すとIDに対応したレコードを返してくれるメソッド)をキャッシュしてみます。

前提となる開発・実行環境の話

僕の環境は下記のような感じです。

  • mac mini 2014
  • PHP 7.1系(5.6以降なら多分動く)
  • postgreSQL 9.6系(サンプルコード動かすのに使ってます)

それほど特殊なことをしているわけではないので、PHP5.6以降(多分もうちょっと古いバージョンでも大丈夫)な環境であれば動作すると思います。

また、組み込み先のプロジェクトは、キャッシュ化したいモジュールが class として定義されていることが前提です。

.phtml 等の htmlなんだかphpなんだか良くわからないコードや、関数という概念が存在しない残念なコード等には組み込むことができません。

早速作ってみる

取り敢えず、コード載せときます。説明とかは後回しで。

キャッシュの仕組みを実装しているコード

まずは、メソッドキャッシュの仕組みを実装しているコードを載せておきます。

コードの中身は後で説明するので、中身わかる方は眺めていただいてもいいですし、よくわからない方は読み飛ばしていただいて大丈夫です。

<?php 
class MethodCache {

    private $_instance;
    private static $_className;


    public function __construct($instance) {
        $this->_instance = $instance;
        $ref = new ReflectionClass($instance);
        self::$_className = $ref->getName();
    }

    private static $cached = Array();
    private function get_cache($key, &$value) {
        if (isset(self::$cached[$key])) {
            $value = unserialize(self::$cached[$key]);
            return true;
        } else {
            return false;
        }
    }

    private function put_cache($key, $value) {
        self::$cached[$key] = serialize($value);
    }

    public function __call($method, $arguments) {

        $calledMethod = self::$_className . '->' . $method . ':' . json_encode($arguments, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        $ret = null;

        if (!$this->get_cache($calledMethod, $ret)) {
            $ret = call_user_func_array(array($this->_instance, $method), $arguments);
            $this->put_cache($calledMethod, $ret);
        }
        return $ret;
    }

    public function __get($key) {

        return $this->_instance->$key;
    }

    public function __set($key, $value) {

        $this->_instance->$key = $value;
    }
}

なお、get_cache() がキャッシュ済データを取得するメソッド, put_cache()がキャッシュに値を追加するメソッドです。

これらのメソッドの中身はサンプル用に適当に作っただけなので(一応、キャッシュするという機能は持っていますが)、Redisなどのミドルウェアを使うように適宜置き換えていただければと思います。 また、実運用ではキャッシュの破棄タイミングなども考慮する必要があるかと思います。

サンプルを作って動かしてみる

ということで、取り敢えずサンプル的なものを作って、実際に動かしてみたいと思います。

なお、phpの5.6以上と、遊び用途で使える適当なRDBが使えることが前提です。

僕の環境は、上の方でも一回書きましたが、php7.1、postgreSQL9.6です。

RDBの準備

まずは、RDBに「id」という名前のprimary key を持つテスト用のテーブルを適当に用意して、データを100件ほどインサートしておきます。 列名などはなんでも構いません。

作業環境を整える

RDBの準備ができたら、ソースの作成に入ります。

まずは適当な作業ディレクトリに移動して、以下のコマンドを打ち込んでください。

mkdir intercepter
mkdir dao
curl -sS https://getcomposer.org/installer | php

続いて、以下のファイルを順に作成、保存してください。

intercepter/MethodCache.php

まずは、前項で載せた、メソッドキャッシュの仕組みを実装しているコードを intercepter/MethodCache.php という名前で保存してください。

composer.json

PHPな方にはおなじみのcomposer.json です。 メソッドキャッシュの仕組み自体には不要なのですが、サンプルとしてDBにつなぐために illuminate-database というモジュールを使用しています。

{
    "autoload": {
        "psr-4": {
            "Dao\\"      : "dao/",
            ""           : "intercepter/"
        }
    },
    "require": {
        "illuminate/database": "^5.4",
        "illuminate/events": "^5.4"
    }
}

./dao/ExampleDao.php

illuminate-databaseのモジュールを利用して、DBから値を取ってくるクラス(いわゆるDao)クラスです。

<?php

namespace Dao;

use Illuminate\Database\Capsule\Manager as Capsule;

class ExampleDao {
    private static $instance = null;

    private function __construct() {
    }

    public static function instance() {
        if (is_null(self::$instance)) {
            self::$instance = 
                new \MethodCache(new ExampleDao());
        }
        return self::$instance;
    }

    public function findBy($id) {
        return Capsule::table('test.my_data')->where('id', $id)->first();
    }
}

./test.php

最後のファイルは、サンプルの本体です。

<?php
require_once __DIR__ . "/vendor/autoload.php";

use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Events\Dispatcher;
use Illuminate\Container\Container;

$capsule = new Capsule;

$capsule->addConnection([
    'driver'    => 'pgsql',
    'host'      => 'localhost',
    'database'  => 'example',
    'username'  => 'noranuk0',
    'password'  => 'password',
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => '',
]);
$capsule->setEventDispatcher(new Dispatcher(new Container));
$capsule->setAsGlobal();
$capsule->bootEloquent();

$dao = Dao\ExampleDao::instance();
for ($times = 1; $times < 10; $times++) {
    $i = 0;
    $timeStart = microtime(true);
    for($i = 1; $i <= 100; $i++) {
        $dao->findBy($i);
    }
    $timePassed = microtime(true) - $timeStart;
    $timePassed *= 1000;
    print("{$times}回目:{$timePassed}ms\n");
}

なお、DB接続の部分のパラメータは環境に合わせて適切に設定してください。

composer の実行

実際にサンプルを動かす前に、composerを使いautoloadの設定と必要なモジュールのインストールを行います。

以下のコマンドをタイプしてください。

composer installer
composer upate

動かしてみようか

ここまでの作業で、サンプルは完成(のはず)です。

早速動かしてみます。

環境により実行時間は異なってくると思いますが、キャッシュが効いている2回目以降は爆速みたいな結果が出てくると思います。

1回目:47.780990600586ms
2回目:0.20217895507812ms
3回目:0.1981258392334ms
4回目:0.22983551025391ms
5回目:0.22292137145996ms
6回目:0.24795532226562ms
7回目:0.1990795135498ms
8回目:0.20503997802734ms
9回目:0.20503997802734ms

既存コードへの取り込み方

高速化したいクラスのインスタンスを生成する際、そのクラスのインスタンスを直接生成するのではなく、new MethodCache(...) で囲います。

self::$instance = 
    new \MethodCache(new ExampleDao());

こんな感じです。

これで、class内に定義されたメソッドは全てキャッシュ対象となります。

クラスのインスタンスメソッドが呼び出されたときに、過去に同一の引数で呼び出されたことがあり(かつキャッシュが残っていれば)元のメソッドは呼び出さず、キャッシュされた値を返します。

原理

例えば、こんなコードが合ったとします。

$dao = new MethodCache(new ExampleDao());
$dao->findBy($id);

findBy()メソッドは ExampleDao に定義されたメソッドで、MethodCache class には該当メソッドがありません。

PHPで、存在しないメソッドが呼ばれると、 __call() というマジックメソッドが呼ばれます。

PHP: マジックメソッド - Manual

今回は、このメソッドの中で、キャッシュがあればそれを返し、なければ元のclassのメソッドを呼び出す、ということをやっております。

課題

class内のメソッドを何でもかんでもキャッシュしたら都合が悪い場合も勿論あると思います。

例えば、取得系のメソッドはキャッシュしたいけど、追加、更新系のメソッドはキャッシュしたくない、などです。

メソッド名で、キャッシュする、しないを判断する仕組みを間に挟んだり、メソッドアノテーション的なもの(PHPにあったっけ・・・?)でなんとかする等、方法はあると思います。

取り敢えず、そろそろ書くのも飽きてきたのと眠くなってきたので、この辺のことはまた今度気が向いたら考えることにします。

取り敢えず、今日の話は、PHPでもAOP的な仕組みを使って、メソッドキャッシュみたいなこともできるよっていう部分の紹介までということで。

最後に

今回の記事の参考にさせていただいた記事の紹介です。

PHPでAOP的にメソッドの実行時間を計測 - Qiita

こちらの記事でメソッドの実行時間を取得する目的で使われている仕組みを、メソッドキャッシュのために使ってみました。

大変参考になりました。どうもありがとうございましたm(__)m

ということで、今回のお話は以上となります。

この記事が、PHPな方々にとって何かの参考になれば幸いです。

詳細! PHP 7+MySQL 入門ノート

詳細! PHP 7+MySQL 入門ノート

世界で一番軽くてシンプル:mac用のシャッフル専用音楽プレーヤー

どうものらぬこです

今日は、とても軽くて、メモリーもほとんど食わないmacで使える音楽プレーヤ(?)を紹介するお話です。

少し前に購入した mac mini にもようやく慣れてきました。

ビデオキャプチャーボード挿してゲーム動画撮りたいとか、TV録画の為にPT3挿したいとか、心はいつまでも高@生の如くエ口ゲープレイを続けたいとか、そういう願望がないのであれば、Windowsmacも変わんないですね。

さて今回は、macで使える「世界で一番軽いかもしれないシャッフル専用音楽プレーヤー」を紹介してみます。

f:id:noranuk0:20170524222935p:plain

macには、iTuneというソフトウェアが最初から付いています。

音楽再生もできるし、写真の管理もできる、iPhoneiPodを持っていればそれらのデバイスとデータを動悸することもできる。

ただ、高機能ソフトウェアの宿命というかなんというか、とにかくメモリー容量を食います。

ライブラリに曲が何も登録されていない状態でも100Mb近くのメモリーを消費しています。

バックグラウンドで音楽再生したいだけなのであれば、もう少し軽いソフトウェアがほしいところです。

というわけで、今回は、超軽量音楽再生ソフトウェアの紹介です。

Windowsにしろmacにしろ、音楽ライブラリーと称してid3タグの情報をDB化して管理してくれる系のアプリが最近の流行りのようです。

でも、そんなのいらないからフォルダ指定したらそのフォルダ以下(サブフォルダ含めて)の曲を順番or適当に再生してくれればそれでいいという人もきっと多いと思います。

今回紹介するのは、そういった人向けのとっっても軽い音楽プレーヤーです。

この音楽プレーヤー、数千曲のmp3ファイルをシャッフル再生中でも消費メモリーは5〜10Mbほどです。

モリー消費が少ないうえ、作業のじゃまにも全くならないのですが、以下のような事はできません

  • ユーザインタフェースがありません。
    • プレイヤーのウィンドウもないし、メニューのステータス領域に常駐もしません。
    • コンソールアプリというわけでもないのでterminalも不要です。
    • 曲が流れていない状態でアプリを起動すると、曲の再生が始まります。
    • 曲が流れている状態でアプリをもう一度起動すると、曲の再生が止まります。
    • スキップや一時停止など、再生と停止以外の操作はできません。
  • 再生中の曲情報(タイトル、アルバム名、作曲者等)は表示できません。
  • もちろんアルバムアートも表示できません。
  • イコライザとかそんな高級機能ありません。
    • でも、音はそこそこいいと思います。
  • アプリダウンロードして実行するだけ、という訳にはいきません。
    • 自分で作ります。シェルスクリプトを書くところからはじめます。
      • 難しいことを知らなくても使えるように、この記事では(ほぼ)コピペでできるように手順を一つ一つ説明しています。

f:id:noranuk0:20181114004909p:plain

曲をBGMとして再生できればそれでいいよっていう方で、もし興味ありましたらこの先を読んでみてもよろしいかと思います。

逆に、シンプルでも見た目がきれいな普通の音楽プレーヤーをお探しの場合、この記事はちょっとお役に立てないと思います。

最初にネタばらし

そこそこなんか色々知っている人向けに先にタネ明かしをしてしまうと、中身は .app化した afplayer です。

ただし、afplayer は「曲を1回だけ再生する」という機能しか備わっていないません。シャッフル再生を実現するために、対象音楽ファイルのパス一覧が記載されたプレイリストファイルをランダムソートして、1曲毎にafplayer を起動する方法を取っています。

以下、手順と原理を簡単に説明します。細かい手順は次項で説明します。

  1. 再生したいmp3ファイル一覧が1行1ファイルの形で書かれたプレイリストファイルを作成します。
  2. 音楽プレーヤーの正体は「afplayer」というCUI(ターミナル上で動く)アプリです。
  3. afplayerには、複数の曲を再生する機能が備わっていないため、ランダム再生や曲リストの再生機能は、シェルスクリプト等で補ってやります。
  4. terminalからそのまま起動するとシェルを1つ専有して邪魔なので nohup ~~~ >/dev/null 2>&1 & で括ってプロセスをshellから切り離します。
  5. 音楽聞いてる時にスリープされると悲しいので、「caffeinate」というコマンドを利用して、再生中のスリープを抑制しておきます。
  6. 曲を止めたいときは killで止めちゃいます。
  7. 毎回コマンド叩くのめんどくさいので、音楽再生中なら停止、再生してなかったら再生開始するシェルスクリプトを作ります。
  8. terminal立ち上げるのもめんどくさいのでアプリ化して、ついでにいい感じのアイコンもつけてあげます。
  9. これをdockに載せて完成。アプリアイコン突っつくたびにシャッフル再生開始>停止 を繰り返します。

作ってみる

アプリを作る

アプリを作るといっても、別にプログラム書いたり開発ツール入れたりとかはいらないです。 なんかうまく動かなかったときのために、多少前提知識はあったほうがいいですが、多分コピペで動くように作ってあります。

というわけで、実作業を開始します。

作業は、ターミナルウィンドウ上でコマンドを叩きながら行うので、取り合えずターミナルを起動してください。

最初にプレイリストファイルを作成します。

諸般の事情により、プレイリストは ~/Music/playlist.txt という名前で用意します。

場所を変えたい方は、このあとで出て来る playlist.txt を参照している箇所をなんかいい感じに読み替えたり置き換えたりしてください。

$ cd ~/Music
$ find ~/mnt/share/MUSIC -name *.mp3 > ./playlist.txt
$ find ~/mnt/share/MUSIC -name *.flac >> ./playlist.txt
$ find ~/mnt/share/download -name *.mp3 >> ./playlist.txt

音楽ファイルの保存場所に合わせて、パスの指定は適切に変更してください。

いったんホームディレクトリに戻り、適当な作業ディレクトリを作ります。

$ cd ~
$ mkdir work
$ cd work

作成するアプリの雛形を作ります。

$ mkdir shufflePlay.app
$ cd shufflePlay.app
$ mkdir Contents
$ cd Contents
$ mkdir MacOS
$ mkdir Resources

大文字と小文字、全角と半角の区別に注意してください。

次に、./Contents/MacOS 内に以下の内容の shellscript を作成し、更に実行権限も与えます。

$ cd MacOS
$ pwd
$ vi play.sh
#!/bin/sh

cd ~/Music

kill `pgrep -lf /play.sh|awk '{print $1}'`
IS_KILLED=$?
pkill afplay

if [ "$IS_KILLED" -ne 0 ]
then
  echo play start
  nohup cat playlist.txt | while read x; do echo -e "$RANDOM\t$x"; done | caffeinate -i sort -k1,1n | cut -f 2- | while read x; do afplay -q 1 "$x"; done >/dev/null 2>&1 &
else
  echo play stoped
fi
$ chmod +x play.sh

なお、hdmiなどで接続したディスプレイの内蔵スピーカーから音を出している方は、「caffeinate -i ... 」の部分を「caffeinate -id ... 」としてください。

ここまでの作業まで完了すれば、作成したshell script を実行することで曲の再生が始まり、もう一度実行すれば曲の再生が停止するはずです。

$ ./play.sh

うまく動かない場合、これまでの手順の見直しや、スピーカー音量の確認などを行ってください。

アプリの挙動を記載した Info.plist というファイルを ./Contents 以下に作成します。

アプリが起動した時に、先ほど作成したシェルスクリプトを動作させるようにしています。

また、アプリのアイコンの指定もこの中で行っています。

$ cd ..
$ vi Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>CFBundleExecutable</key>
    <string>play.sh</string>
    <key>CFBundleIconFile</key>
    <string>play.icns</string>
  </dict>
</plist>

最後に、アプリアイコンを作ります。

めんどくさかったらこの手順はスキップしても良いのですが、せっかくなのでアプリのアイコンもそれっぽやつを作りましょう。

絵心がある方は自分で描いてもいいですし、そういうのは先人の資産を活用したいという方は「音楽プレーヤ アイコン」などの検索ワードで検索すれば色々と出てきます。著作権には気をつけつつ、その辺利用するのも一つの手とお思います。

画像の形式は、png が使えればいいのですが、.icns というファイル形式にする必要があります。

下記のサイトを利用すれば、webブラウザで、png 形式の画像を icns ファイルに変換することができます。

ICON CONVERTER: Convert PNG to ICO and ICNS online - iConvert Icons

作成した.icns ファイルは、./Contents/Resources/ ディレクトリに play.icns という名前で保存します。

ここまでの作業で、shufflePlay.app ディレクトリの中はこんな感じになっているはずです

.
└── Contents
    ├── Info.plist
    ├── MacOS
    │   ├── nohup.out
    │   └── play.sh
    └── Resources
        └── play.icns

以上で完成です、作ったアプリを Finderなどでアプリケーションディレクトリに移動すれば LaunchPadにアプリが現れるはずです。もちろん アプリをdockにドラッグ&ドロップすれば、dockにも追加することもできます。

試しに、アプリを起動してみると音楽再生が始まります。もう一度起動すると停止します。

以後、アプリを起動するたびに、曲の再生開始>停止>再生開始>停止 が繰り返されるはずです。

参考サイト

今回、参考にさせていただいたサイト様です。

うまく動かない、ということがあれば、こちらのサイトも参考にしてみてください。

caffeinate - Macのスリープを抑制できる純正コマンド | ソフトアンテナブログ

技術/MacOSX/シェルスクリプトを".app"("Bundle")化する - Glamenv-Septzen.net

徹底的にソフトウェアで豊かな音を奏でてみよう - ザリガニが見ていた...。

テキストをランダムにソートする - 揮発性のメモ2

というわけで、今回の記事はここまでです。

この記事が、皆様の音楽ライフの一助となれば幸いです。

ちなみに、僕が使っているPC用のスピーカーがこちらです。若干低音強めですがいい音で鳴らしてくれます。

最後までお読みいただいてありがとうございました。

うわっ…私のmac、重すぎ…?ホイール操作のもたつきを完全に解消する方法とは

どうも、のらぬこです。

今回は、mac minimac book、iMacなどをUSB接続のWindowsホイールマウスで使っていて、なんかブラウザやOfficeソフト、エディタソフトなどのスクロールがもたつくなーっと感じていたら、それはmacの性能が低いからではなく、macOSがダメなせいです、という話をします。

この記事で紹介する対策アプリを入れれば改善できます。

mac mini、ブラウザがなんか重い・・・

さて、前回記事の話題となった先日購入したmac miniですが、、、

Windows機と比べると、

  • 標準のファイラー*1が使いづらい
  • samba接続がやたら不安定
  • フロントにUSBが1つもないのはどうなの

などなど、色々物申したいことはあるのですが*2iPhoneアプリ開発というかswiftの勉強目的で毎日頑張って使っております。

さらに、使用開始してからなんとなく感じていたのですが、Webブラウザの動きがなんか重いのです。

ページロードが遅いとか、レンダリングが遅いとか、javascriptが遅いというか、なんかスクロールするとすごいもたつく感じです。

しかも、タブの数、ページによらず常にもったりです。

しばらく使っていて、なんとなく原因がわかってきました。

マウスのホイールをゆっくり回すと全くスクロールしない!

Windowsユーザからすると、早く回そうがゆっくり回そうが「マウスホイール一目盛=3行分スクロール」*3が常識なのですが、macの場合はどうやら違うようでした。

macの場合、ホイールにも加速の仕組みがあるようで、早く回すと高速スクロール(ひと目盛りあたりたくさんスクロール)、ゆっくり回すとゆっくりスクロール(ひと目盛りあたりちょっとだけスクロール)、そして超ゆっくり回すと全くスクロールしないようにできているのだとか。

更に、高速回転、低速回転、超低速回転の判断がつかないマウスホイール最初のひと目盛りでは全くスクロールしないようになっています。

初動が遅れるので当然動きがもったりした感じになる。

もうね、アホかと。お前ら実装し終わってテストして本当にこれでいいと思ったのか、小一時間ほど問い詰めたい気分になりました*4

解決方法

どうもこの現象、USB接続のホイールマウスのみで発生するようです。

嘘でした、Bluetoothマウスでももったりした動きします。

なので、(あれば)Bluetoothマウスmagic mouse(2)、magic trackpadなど、USB接続ホイールマウス以外のデバイスを使えば、改善するのだと思います(未確認)。

なお、普通のホイールマウスを使いたい場合、USB接続のマウスであれば、USB overdrive というアプリを使うと ホイールの動きをWindowsと同じような感じに変更することができるようになります。

まずは、以下のサイトから usb overdrive をダウンロードし、インストールしてください。有料アプリですが、無料でも使い続けることはできるようです。 ©1999-2017 Alessandro Levi Montalcini

インストール後、再起動を求められたら素直に再起動しましょう。

再起動後、画面左上のりんごアイコンなどから「システム環境設定」を開くと、USB overdrive という項目が増えているのでそいつを選択します。

f:id:noranuk0:20170518010540p:plain

設定画面が表示されるまで、10秒ほど待たされます。画面が表示されてから10秒後、真ん中の「Register Later」 というボタンが押せるようになります。

なお、「Buy Online」からお金を支払って登録コードを受け取ればこの制限はなくなると思います。

f:id:noranuk0:20170518010526p:plain

設定画面が表示されたら、[Wheel up]、[Wheel down]を以下のように設定してください。

f:id:noranuk0:20170518010556p:plain

Direction(方向)は、Windows風なら up => UP down => DOWN で、mac風ならその逆です。 このアプリを入れると、「システム環境設定」→「マウス」のスクロール方向の設定はこのアプリの設定で上書きされます。

speedは、Windowsだと3Lineですが、体感で5Lineくらいがちょうどよいと感じました。お好みで微調整してください。

また、画面左下のSpeed(こちらはポインタ移動速度です)、Acceleration(加速度)の設定も、チェックの有無にかかわらず「システム環境設定」「マウス」の設定は無視されます。標準の設定から変更している場合、こちらで設定し直してください。

macホイールマウスで使っていて、なんかスクロールが遅れる、ホイール操作がなんかもたつく、ブラウザの動きが重いって常々感じている方がおられましたら、ぜひ一度お試しください。

今回の話は以上となります。

お読みいただいてありがとうございました。

*1:Windowsで言うエクスプローラmacで言うFinder

*2:というか、Windows機とmac機、普段使いとしてどちらか一台しか選べないとしたらmacを選ぶ理由は何もないと思うくらいには好きじゃないです。mac

*3:設定で変えられたかも

*4:僕は、magic mouse買わせたいからわざとそうしたんじゃないかと疑ってます