PR

macでエコなタイマー・ストップウォッチ!アプリ不要です。

macOS Venturaで時計アプリが搭載されました。この時計アプリは世界時計、アラーム、ストップウォッチ、タイマーの機能が内蔵されています。この時計アプリで全て解決できます。以下macOS Ventura以前のmacOSには驚くことに時計アプリ内蔵されていませんでした。時計アプリの代替えに何が利用できるのかをまとめた記事になっています。

macOS Ventura以降のmacOSをご利用の方は標準アプリの時計アプリを利用してください。

macOSには標準でタイマーやストップウォッチぐらいあっていいと思いませんか?ないんですよね〜。

日付・日時指定で通知がほしいって場合は、標準アプリのリマインダーやカレンダーで十分満足できます。5分後にアラームを鳴らしたい、かかる時間を計測したいと単純なことは別途アプリが必要になったりします。

腕時計やiPhone標準の時計アプリからストップウォッチやタイマーを使っていませんか?こっちの方が便利ですから当然使っている方多いと思います。100円均一でアラーム・ストップウォッチ・タイマーがついた時計、ストップウォッチも買えるのに標準でついていないなんてって思ってしまいますね。

MacBook Air M2 2022購入しました。アップルシリコン製のMacはiPhoneのアプリが動かせます。Apple社のiPhone用時計アプリ、これを使えば全て問題解決!?と思ったんですが・・・

Appleが提要しているiPhone用アプリはNGでした。App Storeにありません。

iPhoneアプリ(Apple製以外)のストップウォッチアプリ(無料・有料)などは見つかります。M1/M2 Macの方はApp Store(iPhone及びiPad Appタブ)で探してインストールできます。Intel Macではこの機能ありません。

Windowsは標準アプリでタイマー、ストップウォッチ機能が付いています。
macでアプリ不要でタイマーやストップウォッチを使う方法の1つにWeb(Javascript)で公開されているサイトにアクセスする方法があります。ストップウォッチの機能は、ミリ秒精度のものが多いですよね。CPUに負荷がかかります。時間を計るだけなんですけど・・・

CPUに負荷がかかるとファンの音がうなり始めますし、熱もすごいことになってくるのでiPhoneや腕時計を使った方が絶対エコ!です。(笑)

これはミリ秒単位で描画している宿命でもありますね。ミリ秒の精度が必要な方はiPhone使った方がいいと思います。米googleでストップウォッチ、タイマーの機能があります。Googleが作ったストップウォッチでも、ファンの音がうなり始めましたよ。
こちらでチェックできます=>米google stopwatch機能(タイマー機能にも切り替えて使えます)

macでエコなストップウォッチはtimeです

止めるまで、経過時間を一切表示しない代わりに、CPUの負担はほぼない簡易ストップウォッチがターミナルから使えます。
time catコマンドです。timeというのは時間計測するコマンドです。catは通常ファイルの内容を見るコマンドですが、ファイルを指定しない場合、入力を受け付ける待ち状態になります。Control+Cキーで終了させることでrealに経過時間が表示されるトリックです。
timeで何もしないコマンド(cat)を動かし続けて、止めるまでの時間を計測することができます。

macyarounanoka$ time cat
^C

real	5m58.123s
user	0m0.002s
sys	0m0.002s

catコマンドはほぼ何もしない入力待受の状態でCPUにほとんどインパクトを与えません。表示も動作もエコです。

macでエコなタイマーは、sleepです。

米google Timer機能は、なぜかCPUの負担が大きかったです。macでCPU負担の少ないエコなタイマは、sleepコマンドで実現できます。sleepコマンドは秒精度です。小数表記でミリ秒も指定することができます。指定秒数経過したら”時間になりました”とか好きな言葉で喋ったり、音を鳴らしたり、通知を出したり、画面をフラッシュさせることもできます。

画面フラッシュは、brightnessというコマンドを入れる必要があるのでここでは省略しています。
コマンドは以下のような形で、ターミナルから指定します。

sleep 60.0 ; 時間が経過したらアクションさせたいコマンド

時間がきたら喋って教えてくれる

時間が経過した後、喋らせることができます。macOSには標準でテキストを音声に変えてくれるsayコマンドがあります。このコマンドで好きなテキストを指定することで喋らせることができます。

sleep 60.0 ; say 時間がきました。

60.0秒経過した後、”時間がきました”と喋ります。

喋る声は、システム環境設定>アクセシビリティから変更することができます。OtoyaさんとKyokoさんが日本語担当です。

macで喋る声はOtoyaさんとKyokoさん

システムの声よりカスタマイズすることができます。sayコマンドで何も音が出ない方はこのボイスがインストールされていない可能性があります。カスタマイズから入手してみてください。(お金を払う必要なしです)

時間がきたら音楽・サウンドでわかる

コマンドラインから音を鳴らすことができます。
単純なビープ音は、以下コマンドです。

sleep 300;tput bel

5分経過した後、ぽんっと音がなります。

何回かビープ音を繰り返したい場合は、以下コマンドです

sleep 180;osascript -e 'beep 3'

3分経過した後、ぽんぽんぽんっと音がなります。

macOS標準の音を鳴らす場合は、以下コマンドです。

sleep 600;afplay /System/Library/Sounds/Ping.aiff

10分経過した後、ピィンっと音がなります。

Pingのほか、Basso、Blow、Bottle、Frog、Funk、Glass、Hero、Morse、Purr、Sosumi、Submarine、Tinkのバリエーションがあります。

繰り返して鳴らしたい場合は、;で区切って複数回コマンドを指定させます。

sleep 600;afplay /System/Library/Sounds/Ping.aiff;afplay /System/Library/Sounds/Ping.aiff

同じコマンドで音楽を鳴らすことができます。鳴らす長さを秒数で指定することができます。

sleep 1800; afplay -t 秒数 ~/音楽.mp3 

30分経過した後、音楽が指定秒数かかります。

時間がきたら通知と音でわかる

通知は、osascriptコマンド(スクリプトコマンド)を使います。

osascript -e 'display notification "時間です" sound name "Ping" '
mac-simple-notification-201802.png

こんな感じで通知が表示され、Ping音もなります。音の名前は、前述のこちらにあるシステム音名が指定できます。

sleep 300.0;osascript -e 'display notification "時間です" sound name "Ping" '

5分経過後、音と通知で知らせてくれます。

タイマーならSiriにお願いできます

「5分経ったら教えて」、「5分後タイマー」とSiriにお願いするとタイマー予約(リマインダー)してくれます。時間が来るとリマインダーの通知音と共に通知が届きます。

mac-timer-reminder-sample.png

Siriは音声入力の他、設定変更することでキーボード入力でお願いすることもできます。
設定と入力方法は以下の通りです。

  1. システム環境設定→アクセシビリティのSiri 「”Siriにタイプ入力”を有効にする」項目をチェック。
  2. Command+スペース長押し、またはメニューバーのSiriアイコンクリックでSiriを呼び出すと「Siriにタイプを入力」と表示されています。このエリアはテキスト入力です。キーボードから入力することでお願いできます。
    mac-timer-siri-input-type.jpg
  3. 音声入力に戻したい場合、「”Siriにタイプ入力”を有効にする」項目をOFFにします。

高性能タイマーやストップウォッチ:Google Chromeのショートカット、ブラウザのブックマーク

HTML(JavaScript)で実現しているタイマーやストップウォッチを呼び出して使う方法もあります。
ターミナルでやるとどうしても貧弱なインタフェースになってしまいます。スタート・ストップボタン、時間表示などの機能を実現するにはアプリが必須です。Google Chromeのショートカット機能でアプリ的(ブックマークに近いです)に作ることができます(Chromeの縦3つの・からその他ツール→ショートカットを作成…)。
「javascript タイマー」、「javascript ストップウォッチ」などと検索すると実現方法や動くサンプルなどが見つかります。javascriptでよく使う関数は精度が悪いです。

探すの面倒な方はgoogle製があります(全画面表示で利用することもできます)。エコではありません。
米google timer機能
米google stopwatch機能

Swift uiでストップウォッチを自作する

ストップウォッチ自体の仕組みは簡単です。macOSでもSwift言語でユーザーインタフェースを含めて自作することができます。例えば「SwiftUIでいこう! – Stopwatchを作る。2」などストップウォッチを作る上での情報を入手できます。Playgroundで作ったプログラムをコマンドとして動かす方法は「fourplusone / main.swift」が参考になります(これを見つけたのでご紹介するstopwatch.swiftが公開できました。できないと思っていたのでありがたい情報でした)。自作したストップウォッチをターミナルから実行するとこのような画面が表示できます。

mac-stopwatch-swift-execute.jpg

こちら↓のソースコードをstopwatch.swiftとして保存します。chmod +x stopwatch.swiftで実行権限を与え、./stopwatch.swiftで実行できます。

#!/usr/bin/env xcrun swift
/* macOS ストップウォッチを実現するswift uiのソースコード 
   create by macyarounanoka
*/
import SwiftUI
import Foundation
// ↓参考:https://stackoverflow.com/questions/28872450/conversion-from-nstimeinterval-to-hour-minutes-seconds-milliseconds-in-swift
extension TimeInterval{
    func stringFromTimeInterval() -> String {
        let time = NSInteger(self)
        //let ms = Int((self.truncatingRemainder(dividingBy: 1)) * 1000)
        let seconds = time % 60
        let minutes = (time / 60) % 60
        let hours = (time / 3600)
        return String(format: "%0.2d時間%0.2d分%0.2d秒",hours,minutes,seconds)
    }
}
class StopWatch : ObservableObject{
    enum Mode {
        case 停止中
        case 動作中
    }
    @Published var mode:Mode = .停止中
    @Published var 経過時間:TimeInterval = TimeInterval(CGFloat(0))
    var 低更新頻度:Bool = false
    var timer = Timer()
    var last = Date();
    func is動作中()->Bool{
        return mode == .動作中;
    }
    func カウンター文字列()->String{
        if( 低更新頻度 ){
            return String(format:"%.1f",経過時間)
        }else{
            return String(format:"%.3f",経過時間)
        }
    }
    func 時分秒文字列()->String{
        return 経過時間.stringFromTimeInterval()
    }
    func start(_ 低頻度:Bool){
        mode = .動作中
        last = Date();
        低更新頻度 = 低頻度;
        let interval = 低更新頻度 ? 0.1 : 0.01;
        timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: { timer in
            let now = Date();
            self.経過時間 += now.timeIntervalSince(self.last)
            self.last = now;
        })
    }
    func stop(){
        timer.invalidate(); mode = .停止中;
    }
    func clear(){
        timer.invalidate(); mode = .停止中;
        経過時間 = TimeInterval( CGFloat(0) )
    }
}
struct ContentView: View {
    @State var text:String = "開始ボタンをクリック"
    @State var エコモード:Bool = false
    @ObservedObject var sw = StopWatch()
    
    var body: some View {
        VStack(alignment: .leading){
            Text(sw.カウンター文字列())
                .frame(width:300,height:200,alignment: .center)
                .font(.custom("Arial", size: 50))
                .background(Color.white)
            Text( sw.時分秒文字列() )
                .frame(width:300, alignment:.center)
            Toggle("エコなモード", isOn: $エコモード)
                .frame(width:300, alignment:.center)
            HStack(){
                if sw.is動作中() {
                    Button(action: {self.sw.stop()
                        self.text = "一時停止中です。開始ボタンで継続スタート"
                    }){
                        Text("停止")
                            .frame(width:100)
                    }
                }
                else {
                    Button(action: {self.sw.start(エコモード)
                        self.text = ""
                    }){
                        Text("開始")
                            .frame(width:100)
                    }
                }
                Spacer()
                Button(action: {self.sw.clear()
                     self.text = "クリアしました。開始ボタンでスタートします";
                }){
                     Text("クリア")
                }
            }.padding(.all).background(Color.blue)
        }
        Text(text)
            .padding(.all)
        Button(action:{ exit(1) }){
            Text("終了")
        }.frame(width:300, alignment:.center)
    }
}
struct MainApp : App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }
}
class GL {
    static public var isCall = false ;
}

let app = NSApplication.shared
app.setActivationPolicy(.regular)
app.activate(ignoringOtherApps: true)
NotificationCenter.default.addObserver(forName: NSApplication.didBecomeActiveNotification, object: nil, queue: .main) { _ in
    if( GL.isCall == false){
        app.mainWindow!.standardWindowButton(NSWindow.ButtonType.closeButton)!.isHidden = true
        GL.isCall = true;
    }
}
MainApp.main()

  1. ラップ機能がないシンプルなストップウォッチです。
  2. トリッキーな技を利用しています。swiftのアップデート等で動かなくなる可能性を秘めています。macOSが認めるアプリではありません。ターミナルから起動してください。動作確認したswiftのバージョン swift –version
    Apple Swift version 5.5.2 (swiftlang-1300.0.47.5 clang-1300.0.29.30)
    Target: x86_64-apple-darwin21.2.0
  3. ご利用にはXcode Command Line Tools ( xcode-select —install )が必要です。約6GB容量を消費します。
  4. エコなモードは100ミリ秒間隔でカウントアップします。CPU使用率13%前後です(mid2015)。
  5. エコなモードをOFFでは、10ミリ間隔でカウントアップします。CPU使用率95%前後です(mid2015)。
  6. カウントアップの精度はDateクラスに依存しています。μ秒の精度です。描画はmacOSの負荷具合によって遅延します。Timer.scheduledTimerの呼び出し間隔は保証されていません(少しアバウトです)。その関係でミリ秒は描画が追いつかないことがあります。
  7. 開始ボタンをクリックすると停止ボタンが表示されます。クリアボタンで0から始めることができます。
  8. 終了ボタンをご利用ください。赤丸×ボタンはありません(消しています)。ターミネイトイベント検出できませんでした。
  9. struct ContentView: Viewの部分で画面パーツを構成しています。色やフォント、大きさ、位置など変更することが可能です。「swiftui Text 色変更」等、パーツ名、やりたいことを変えながらググると情報にアクセスしやすいです。Playgroundを利用するとトライアンドエラーで試せます。

まとめ

タイマーが必要、ストップウォッチが必要って時にmacさえあればできる方法をご紹介してきました。単機能なので使い勝手は悪いです。その代わりほとんどCPU使わないので暑くなったら使ってみてください。

タイトルとURLをコピーしました