工作と競馬2

電子工作、プログラミング、木工といった工作の記録記事、競馬に関する考察記事を掲載するブログ

Flutter側で、Androidのビルド日時を取得する

概要

Flutter側から、Androidのビルド日時を取得するためのコーディング方法を整理した。




背景と目的

Flutterアプリを作成する際、ビルド日時をアプリ画面で表示したかった。そのため、方法について調べ、整理しておく。



詳細

0. 環境


1. Android

以下の2つのファイルに必要な設定、コーディングを行う必要がある。

build.gradle

android/app/build.gradleに、外部から参照できるように設定を追記。

andriod {
    中略
    :
    defaultConfig {
        :
       中略

        // ビルド日時を Context#getString で参照できるようにする
        resValue("string", "build_date", System.currentTimeMillis().toString())
    }

kotlin側ソースコード

MainActivity.kt を以下のように変更。

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.text.SimpleDateFormat
import java.util.Locale
import kotlinx.coroutines.ExperimentalCoroutinesApi

// class MainActivity: FlutterActivity()

class MainActivity: FlutterActivity() {
    @OptIn(ExperimentalCoroutinesApi::class)
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "Channel").setMethodCallHandler { call, result -> 
            
            // ビルド日時
            val buildDate = getString(R.string.build_date).toLong()
            // 日付をフォーマットする
            val simpleDateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault())
            val formatDate = simpleDateFormat.format(buildDate)

            when (call.method) {
                "getBuildDate" ->
                    result.success(formatDate)
                else ->
                    result.success(null)
            }
        }
    }
}

2. Flutter側ソースコード

Flutter側で取得するサンプルは以下。MethodChannelという手段を用いて、MainActivityで定義したメソッドを呼び出す。

Future<String> _getBuildDate() async {
  const MethodChannel channel = MethodChannel("Channel");
  String? resultText = await channel.invokeMethod("getBuildDate");
  return resultText ?? "null";
}

void main() async{

  String? buildDate = await _getBuildDate();
  print("build date=${buildDate ?? 'null'}");

  runApp(const MyApp());
}


参考

takusan.negitoro.dev rightcode.co.jp



まとめと今後の課題

Flutter側から、Androidのビルド日時を取得する方法が分かった。アプリ作成に活用する。


FDTD法でスピーカーエンクロージャー形状による影響をシミュレーション

概要

FDTD法で、スピーカーエンクロージャー形状による影響をシミュレーションした。




背景と目的

近々、作業デスクに設置するタイプの小型スピーカーの製作をしたいと考えている。およそ2年前に、バッフルの端を丸めた場合の効果について、FDTD法を用いて検証した。

dekuo-03.hatenablog.jp

今回は、端を丸めるのではなく、バッフル面自体を大きく丸めたような形状を検討している。バッフル全体の丸めによる効果を検証しておく。



詳細

条件

0. エンクロージャー形状のイメージ

以下のように、バッフル面全体が丸みを帯びている形を考える。


1. 条件設定

1.1 基本条件

最大解析周波数を10kHzとし、クーラン数を 0.58に決め、空間離散化幅が最大周波数の1波長の1/10となるように定めた。

  • 領域の大きさ: 1m×1m
  • PML: 64層
  • 音速: 343.5m/s
  • 密度: 1.205kg/m3
  • クーラン数: 約0.58(1 / √3)
  • 空間離散化幅: 3.4mm
  • 時間離散化幅: 5.8usec
  • シミュレーション時間: 7.5msec
  • 次元: 2次元

1.2 信号

中心時刻を変えて低周波成分に制限した場合と、より高周波成分を含む場合の2種類を用意する。

  • 波形: ガウシアンパルス
  • 中心時刻: 1msec および 0.33msec
  • ガウシアンパルス係数: a = 20.000e5 / (t0 * t0 * 400 * 400)


2. 対象のエンクロージャ

比較しやすいように、以下の3種類を用意する。

3. シミュレーション音場

音場の中心に音源(エンクロージャーバッフル面に取り付けたスピーカー位置を模擬)が来るように、エンクロージャーを配置する。また、受聴点として、作業デスクに設置し、デスクに着席することをイメージして、バッフルから約45cm離れた正面とする。


3. 結果

音圧分布の様子と、音圧時間波形、スペクトルを記載する。

3.1 低周波

3.1.1 球体エンクロージャ

エンクロージャー後面に回り込んだものが再度前面に戻ってくるが、高周波が含まれないため、エンクロージャーの外形に起因する細かな回折波はない。

3.1.2 ラウンドエンクロージャ

エンクロージャーの外形に起因する顕著な回折波はなく、球形とそれほど大きく変わらない。

3.1.3 直方体エンクロージャ

エンクロージャーの外形に起因する顕著な回折波はなく、球形とそれほど大きく変わらない。

3.2 高周波

3.2.1 球体エンクロージャ

各所で回折は発生するものの、エンクロージャー後面に回り込んだものが再度前面に戻ってくる影響の方が大きい。

3.2.2 ラウンドエンクロージャ

各所で回折は発生するものの、球形と大差ないレベル。ラウンド形状のバッフルの効果があるといえる。

3.2.3 直方体エンクロージャ

バッフル端での回折波の発生がよくわかる。



まとめと今後の課題

バッフル面の丸め効果について検討できた。製作するスピーカーの設計を進めていく。


条件

Flutter Riverpod基礎

概要

Flutterで、Riverpodを使うための基礎的なことの自分用メモ。




背景と目的

Flutterでは、状態管理の実現方法はいろいろあるが、

  • ビュー、ロジック、ステートの分離
  • ステータス変数へのグローバルなアクセス

を実現する方法として最も用いられていると思われるRiverpodを使いたい。そこで、Riverpodを使用するための基礎的なことの自分用にメモする。



詳細

0. インストール

flutter pub add flutter_riverpod


1. 前提を整理

説明のため、以下を前提とする。

  • MyWidget: ボタンとテキストを持つWidgetクラス
    • インクリメントというボタンを押したら、上部のテキスト内の数値が増える
  • MyWidgetState: 状態を管理するクラス
    • counterという変数を持つ
  • MyWidgetStateNotifier: ロジックを管理するStateNotifierクラス
    • counterを変化させるincrementというメソッドを持つ


1. フォルダ分け

ビュー、ステート、ロジックをそれぞれ分離してフォルダ分けすることが一般的らしいので、MyWidgetに関連する一連のファイルは、以下の構造で格納する。

  • screen: ビュー=見た目を定義する
  • state: 状態を定義する
  • view_model: 状態を変化させるロジックを定義する
lib/
  screen/
    my_widget.dart
  state/
    my_widget_state.dart
  view_model/
    my_widget_view_model.dart


2. ビュー

myWidgetStateProvider という変数を利用できることで、ボタンウィジェット、テキストウィジェットでMyWidgetStateの値を利用できる。

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_widget_project/state/my_widget_state.dart';
import 'package:my_widget_project/view_model/my_widget_view_model.dart';

// ウィジェット全体
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return ProviderScope(
      child: Scaffold(
        body: Container(
          padding: const EdgeInsets.all(20),
          child: const Column(
            children: [
              MyTextWidget(),
              MyButtonWidget(),
            ],
          ),
        ),
      ),
    );
  }
}

// テキストウィジェット
// counterを表示
class MyTextWidget extends ConsumerWidget {
  const MyTextWidget({super.key});
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final int counter = ref.watch(myWidgetStateProvider).counter;
    return Text('$counter');
  }
}

// ボタンウィジェット
// ボタンを押すとincrementメソッドを呼び出してcounter値を変化させる
class MyButtonWidget extends ConsumerWidget {
  const MyButtonWidget({super.key});
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final Function increment = ref.read(myWidgetStateProvider.notifier).increment;
    return ElevatedButton(
        onPressed: () => increment(), child: const Text("MyWidget"));
  }
}

// MyWidgetStateへのグローバルアクセスを提供するもの
final myWidgetStateProvider =
    StateNotifierProvider<MyWidgetStateNotifier, MyWidgetState>(
  (ref) => MyWidgetStateNotifier(),
);


3. ステート

my_widget_state.dartの中身として、counterという変数を持つクラスを定義。copyWithというメソッドでcounterの新しい値を受け取ってstateを更新する。

class MyWidgetState {
  const MyWidgetState({this.counter = 0});

  final int counter;

  // copyWithメソッドを定義する
  MyWidgetState copyWith(int counter) => MyWidgetState(counter: counter);
}


4. ロジック

my_widget_view_model.dartの中身として、StateNotifierを継承したクラスを定義。incrementというメソッドで、stateを更新するcopyWithへのアクセス手段を提供する。

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:my_widget_project/state/my_widget_state.dart';

// ロジック
class MyWidgetStateNotifier extends StateNotifier<MyWidgetState> {
  MyWidgetStateNotifier()  : super(const MyWidgetState());

  void increment() {
    state = state.copyWith(state.counter + 1);
  }

}


5. main.dart

MyWidgetをインポートして利用する。

import 'package:flutter/material.dart';
import 'package:flutter_routing/screen/my_widget.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      // MyWidgetを使う
      home: const MyWidget(),
    );
  }
}



まとめと今後の課題

Riverpodを使った基礎的なコーディング方法を整理できたので、活用していきたい。