2021年 11月 04日
[BASIC!] スマホのBASIC言語でツールを作る2(10) [意外と難航したGUI調整] (11/4)
%********************
%【機能】音声メモプログラム
%
%●DBに音声入力で認識した文字列を格納する
%●処理種別も音声入力で行う
%
%■音声コマンド
% メモ:格納データの逐次表示(Androidのトースト。画面下にpopup)
% 追加:データの末尾追加
% 削除:指定データの削除
% 逆:表示順の反転(起動時正順-古い順)
% 検索:メモを検索する
% 解除:検索キークリア
% クリア:全データ削除
% 終了:プログラム終了
%■クリア確認コマンド
% 実行:OK
% キャンセル:取消
%■追加、削除、検索中断コマンド
% キャンセル
%■制限
% メモ最大数=20
% 日本語認識はN2 TTSインストール要
% 初回起動時はSQL.new_table有効、以降無効化要
% DBファイル格納場所や音声認識はAndroidやBASIC!、
% Google音声認識の仕様に従う
% 追加削除検索コマンドで内容が単独の"キャンセル"は操作不可
%■データ構造
%-----------------
%|メモ内容 |システム時刻 |
%-----------------
%■区分線の説明
%***仕様ヘッダ範囲
%===セクション区切り(定義・初期化・メイン・エラー)
%---機能毎(関数・処理)
%
%********************
%====================
% 定義
DEFINED:
%------------------------
% グローバルテーブル定義
%------------------------
BUNDLE.create BP % 関数用(下記関数で参照のためここ)
%メンバー定義はAPKビルド時参照前にここで行う
%メンバー(タグ)を定義・初期値代入
BUNDLE.put BP,"help","\n"+"(メモ 追加 削除 終了 [クリア] 逆 検索 解除)\n今の検索キー:" %改行付きヘルプ固定文字列
BUNDLE.put BP,"mkey","" %検索キー
BUNDLE.put BP,"ps",0.7 %ポーズ待ち時間速度
%------------------------
% 関数
%------------------------
% BASIC!は関数を先に定義しないとコールできない
% 関数"定義"は1回しか呼び出せない(DEFINEDラベルにジャンプするとエラー)
%------------------------
% タッチ無しpause関数
%------------------------
% BP:関数用グローバルテーブル
% s:秒
FN.def fn_paus(BP,s)
%グローバルテーブル該当メンバーからローカル変数へ値を取り出す
BUNDLE.get BP,"ps",ps
for k=1 to s*2 %0.5秒毎なら2
pause ps*500 %*500=0.5秒
next k
FN.end
%------------------------
% ウエイト、回数付きメッセージ関数(トースト)
%------------------------
% BP:関数用グローバルテーブル
% s1:前ウエイト秒数
% str$:メッセージ文字列
% n:表示回数
% s2:後ウエイト秒数
% ※BPで引き渡すのはヘルプ無しの単独でコールする場合があるため
FN.def fn_msg(BP,s1,str$,n,s2)
if s1>0 then fn_paus(BP,s1) %0は無限なので
for i=1 to n %消えないので回数で表示時間になる
POPUP str$,0,1000,0 %トースト表示はバックグラウンド処理 (x,y)=(0,1000),無し/0=2秒 1=4秒
next
if s2>0 then fn_paus(BP,s2) 【←ココ *****】
FN.rtn 0 %関数は真偽を返せない(代わりにデフォルトの0をセット-偽の0ではない)rtnは無くても良い
FN.end %関数の定義終了と実行終了を兼用
%------------------------
% ヘルプ、検索キー付きメッセージ関数
%------------------------
% BP:関数用グローバルテーブル
% s1:前ウエイト秒数
% str$:メッセージ文字列
% n:表示回数
% s2:後ウエイト秒数
FN.def fn_msgh(BP,s1,str$,n,s2)
%関数はクローバル変数を直接参照出来ない
%変数は全てローカルか仮引数になる
BUNDLE.get BP,"help",help$
BUNDLE.get BP,"mkey",mkey$
fn_msg(BP,s1,str$+help$+mkey$,n,s2) %ウエイト、回数付きメッセージ関数にヘルプ+検索キー追加
FN.end
%====================
% 宣言(※)・初期化
% ※再度INITを呼べる処理をここに置く(REDIMなど)
INIT:
Dim mm$[2,20] %SQL結果用配列(Item,record)
m$="" %メモ内容
s$="" %システム時刻
c$="" %コマンド
w$="" %コマンドワーク用
mdir=1 %メモ表示順 1:正順 -1:逆順
mkey$="" %検索用
i=0 %for用
n=0 %メモ表示順ワーク用
g$="" %ワーク用
List.create s,Li_talk %音声入力用(s:文字列⇔n:数字)
m1$="" %SQLワーク
m2$="" %SQLワーク
%------------------------
% 初期処理
%------------------------
TTS.init %音声出力初期化
fn_msgh(BP,2,"音声メモにようこそ。",1,3)
fn_msgh(BP,0,"認識するのは以下の8つです。",1,3)
%====================
% メインループ
%MAIN_LOOP:
% 必要なラベル(INITしないなど)
while 1 %無限ループ
%Main
gosub MAIN
pause 1 %1ミリ秒待ちでCPU負荷軽減
repeat
%LOOP-END:
% ループ終了
%====================
% エラー処理
while 0 %誤ってメインループを脱出しても正常時は実行しない
%on error goto の飛び先
ERR_INITR:
% 必ず脱出する
repeat
end
% プログラム終了
%====================
%------------------------
% サブルーチン
%------------------------
% メイン
MAIN:
% GOSUB main
%関数用グローバルテーブルにセット
BUNDLE.put BP,"mkey",mkey$
fn_msgh(BP,0,"🎤命令してください⇒",1,4)
STT.listen "🎤命令してください⇒" %音声入力
STT.results Li_talk %認識結果をリスト型変数へ格納
LIST.get Li_talk,1,c$ %1パターン目を取り出す
SW.begin c$
SW.case "メモ"
gosub MEMO
SW.break
SW.case "追加"
gosub APPEND
SW.break
SW.case "削除"
gosub DELETE
SW.break
SW.case "クリア"
gosub CLEAR
SW.break
SW.case "終了"
gosub END_L
SW.break
SW.case "逆"
gosub REVERSE
SW.break
SW.case "解除"
gosub SEARCH_CLEAR
SW.break
SW.case "検索"
gosub SEARCH
SW.break
SW.case "ヘルプ"
SW.break
SW.case "キャンセル"
SW.break
SW.default
gosub ELSE_L
SW.end
return
%------------------------
% メモ
MEMO:
% GOSUB memo
m$="%"+mkey$+"%" %検索文字列%と加工して引数にセット
gosub SQL %SQLサブルーチン
fn_paus(BP,1) %割込み検知無し1秒ウエイト
%SQL結果用配列からメモ内容読み込み
for i=1 to 20 %BASIC!の配列は1から始まる
n=21*(1-mdir)/2+mdir*i %mdir=1:i=1->20/mdir=-1:i=20->1
%空になるまで
if mm$[1,n]="" then F_N.continue
g$=mm$[1,n]+" - "+using$("","%tY/%<tm/%<td",INT(val(mm$[2,n]) ) ) %yyyy/mm/dd
%2回表示でゆっくり読ませる
fn_msg(BP,0,g$,2,5) %Item
next i
fn_msg(BP,0,"メモは以上です。",1,2)
return
%------------------------
% 追加
% GOSUB append
APPEND:
g$="🎤追加内容を教えてください⇒"+"\n"+"(やめる時はキャンセルと命令してください)"
%2回表示でゆっくり読ませる
fn_msg(BP,0,g$,2,4)
STT.listen "🎤追加内容を教えてください⇒" %音声入力
STT.results Li_talk %認識結果をリスト型変数へ格納
LIST.get Li_talk,1,m$ %1パターン目を取り出す
if m$="キャンセル" then
fn_msg(BP,0,"追加をキャンセルしました。",1,2)
else
s$=str$(time()) %システム時刻
fn_paus(BP,1)
gosub SQL %SQLサブルーチン
fn_paus(BP,2)
endif
return
%------------------------
% クリア
% GOSUB clear
CLEAR:
g$="🎤すべてのメモを消去します。\nよろしければ実行、やめるならキャンセルと命令してください"
%2回表示でゆっくり読ませる
fn_msg(BP,1,g$,2,1)
STT.listen "🎤すべてのメモを消去します。"+"\n"+"実行かキャンセルを選んで下さい" %音声入力
STT.results Li_talk %認識結果をリスト型変数へ格納
LIST.get Li_talk,1,w$ %1パターン目を取り出す
if w$<>"実行" then
fn_msg(BP,0,"クリアをキャンセルしました。",1,3)
else
gosub SQL %SQLサブルーチン
fn_paus(BP,5)
endif
return
%------------------------
% 削除
% GOSUB delete
DELETE:
g$="🎤削除内容を教えてください⇒"+"\n"+"(やめる時は\"キャンセル\"と命令してください)"
%2回表示でゆっくり読ませる
fn_msg(BP,1,g$,2,4)
STT.listen "🎤削除内容を教えてください⇒"+"\n"+"中止はキャンセル" %音声入力
STT.results Li_talk %認識結果をリスト型変数へ格納
LIST.get Li_talk,1,m$ %1パターン目を取り出す
if m$="キャンセル" then
fn_msg("削除をキャンセルしました。",1,2)
else
fn_paus(BP,1)
gosub SQL %SQLサブルーチン
fn_paus(BP,2)
endif
return
%------------------------
% 終了
% GOSUB end_l
END_L:
END
return
%------------------------
% その他
% GOSUB else_l
ELSE_L:
fn_msg(BP,0,"違う言葉で命令してください",1,2)
TTS.speak "違う言葉で命令してください"
fn_paus(BP,4)
return
%------------------------
% 逆
% GOSUB memo direction reverse
REVERSE:
mdir*=(-1)
if mdir=1 then
fn_msg(BP,0,"メモ表示を古い順にしました。",1,3)
else
fn_msg(BP,0,"メモ表示を新しい順にしました。",1,3)
endif
return
%------------------------
% 検索解除
% GOSUB keyword clear
SEARCH_CLEAR:
mkey$=""
fn_msg(BP,0,"検索を解除しました。",1,3)
return
%------------------------
% 検索
% GOSUB keyword set
SEARCH:
g$="🎤検索キーを教えてください⇒"+"\n"+"(キャンセルで中断します)"+"\n"+"今の検索キー:"+mkey$
%2回表示でゆっくり読ませる
fn_msg(BP,0,g$,2,4)
STT.listen ”🎤検索キーを教えてください⇒”+"\n"+"中止はキャンセル" %音声入力
STT.results Li_talk %認識結果をリスト型変数へ格納
LIST.get Li_talk,1,m$ %1パターン目を取り出す
if m$="キャンセル" then
fn_msg("検索をキャンセルしました。",1,2)
else
mkey$=m$ %検索キーを保存する
endif
return
%------------------------
% データベース
% GOSUB SQL
SQL:
%POPUP m$
SQL.open DP,"memo.db" %Database Pointer
%初回起動時有効化 SQL.new_table DP,"tmemo","memo","systime"
SW.begin c$
SW.case "メモ"
%検索キーが空なら
if mkey$="" then
SQL.query CP,DP,"tmemo","memo,systime" %Cursor Pointer,"table_name","search item1,item2"
else
SQL.query CP,DP,"tmemo","memo,systime","memo LIKE "+"'"+m$+"'" %select * from tmemo where memo LIKE '%m$%'
endif
SQL.next fe,CP,m1$,m2$ %flag of end record, memo string, systime string
for i=1 to 20
if (!fe) then
mm$[1,i]=m1$
mm$[2,i]=m2$
%print using$("","%.0f,%s,%13.0f",i,m1$,val(m2$)) %Standard BASIC! numbers are floating point.
SQL.next fe,CP,m1$,m2$
else
mm$[1,i]=""
mm$[2,i]=""
endif
next
SW.break
SW.case "追加"
SQL.insert DP,"tmemo","memo",m$,"systime",s$
SW.break
SW.case "削除"
SQL.delete DP,"tmemo","memo="+"'"+m$+"'" %where memo='m$'
SW.break
SW.case "クリア"
SQL.delete DP,"tmemo"
SW.break
SW.default
SW.end
SQL.close DP
return
%------------------------
この大変さは自分でも予想がつかなかったことから、某ソシャゲのマネージャーかつプログラマーも、GUI配置改善案が簡単に終わらない理由が、訳わからないわけだわ~

