chakokuのブログ(rev4)

テック・コミック・DTM・・・ごくまれにチャリ

執筆の気づき

自分は某原稿を書いている。製作記事というか。。採用の可否は分からないが。
熱い人間ではないし、すぐに仕事に取り掛かれるタイプではない。
だから、、まずは延々と逃避が発生する。逃避とは。。スマフォでニュースを読んだり、無料のKindleマンガを読んだり。。
そして段々と時間が経過して夕方になってくる。マンガも読んでると疲れてくる。読み飽きるとむっくり起き上がって少しだけ書き始める。
だが、、原稿を書いていて気付いたのは、書き始めは深みがなくてショボくても、書いているうちに文章が文章を生み出すというか、書いているとネタを思い浮かぶこともある。自分の才能なんてプロの作家の何万分の一でしかないが、、
プロの作家のコメントで、「登場人物が勝手に動きだすのでそれを記録しているだけなんです。」という言葉を思い起こさせる。(プロの何万分の一だが)、書いてるネタが別のネタを呼んできてくれるというか。。だから、まずは手を動かすべきなのだ。ショボくても書いてみる。書いてるうちにちょっとした気づきがあって、小さなヒントから何かが生まれだす。
また、自分が書いてるのは製作記事なので、プログラムを書いて動く必要がある。プログラムは楽しく作業できる部分も多い。だから、あまりしんどいとか想像したり考えたりせずに目の前の小さな簡単なプログラムをまず組む。簡単なプログラムを組んでるうちに(すなち、頭を動かす前に手を動かしていると)それが刺激となって、次のひらめきがやってくる(かもしれない)

結論:課題を想像してしんどくならない。むつかしいことをやりだす前に、目の前の簡単な事から始める。頭を動かす前に手を動かす。そうすると、ちょっとだけ進んだ部分が自分へのヒントや刺激となって、次のひらめきを引っ張ってきてくれる。(かもしれない)

■追記
よくやりがちなのは、、この課題が終わるまでは〇〇(主に楽しいこと)は控えようとか、〇〇はこの記事を書き終えてからにしようとか、自分に言い聞かせるのだが、、それは禁欲で自分を縛ってるわけで、課題がそんなに辛いのか?となる。だから、、そこは楽しい〇〇と連携させず、課題は課題、楽しいことは楽しいことと、分けて考えよう。だから、、?? 原稿書き終えるまでは控えておこうと思っていた、Spireの有料オンラインレッスンを発注してしまおう。。(原稿終わってないけど)

ソフトシンセ Spireを買った

今ちょうど割引中でお安くなっているので、Spireを買ってしまった。
自分はEDMとか全然分かっていないが、できたら音作りもやりたいと思って、一定の評価がされているSpireを買いました。GUIが整理されていて音作りもやりやすいそうなので。。*1
試行錯誤するのもいいかもですが、、一気にノウハウを習得したいので、チュートリアルビデオもオンラインで買ってしまおうと思っています。。どうなることやら。
以下がFL Studio上で起動したSpire画面(早速スキン変えてます)
f:id:chakoku:20210519171625p:plain:w500

ちなみに、、買ったのはPlugin Boutique. という海外のサイトです。普段2万のところ、セール中は1万4千円らしい。日本でもセールしていますが、日本のサイトで買うと、もうちょっと高いですね。

■追記
有料のチュートリアルコンテンツ
How To Use Spire by Reveal Sound with Echo Sound Works | Tutorial 01 - Overview
約10ポンド

Producer's Guide To Spire, Producer's Guide To Spire plugin, buy
値引き期間中で1300円


Sound Design and Synth Fundamentals - YouTube

*1:FL-Studioにもシンセ入ってますが、よくわからん。FL-Studioでは、Sytrusがメインシンセと思ってますが、SytrusはFM変調シンセのため、さらに分からない・・どんな音になるのか想像つかない。単に理解不足なのでしょうか

AmazonのAudibleを試す 「独学大全 絶対に「学ぶこと」をあきらめたくない人のための55の技法」

Amazonから、「Audibleのお試しができる」とメールで案内が来ていた。お試しを申し込むとコインが2つもらえて、2種類のコンテンツを視聴できる(コイン2つというのは誤解だったようです)。話題になってる独学大全*1をAudibleで注文してみた(今はお試し期間中なので、無料で視聴できる)。
Audibleで発注すると、音声データを視聴する権限が与えられ、書籍の中の図表がPDFでダウンロードできる。一方、書籍の本文は入手できない。だから、、本のテキストを表示しながら、音声を聴くことはできない。ラジオで放送される、朗読の時間ようなものかと。活字を見ながらでないと落ち着かない人間なのですが、音声だけでどれぐらい学べるのかもうちょっと続けたいと思います。

■視聴での気づき、メモ
視聴しているコンテンツ:
独学大全 絶対に「学ぶこと」をあきらめたくない人のための55の技法
(聞き終わるまで16時間となっています)

冒頭に無知君と親父さんの対話があるのですが、、この部分は聴いていて面白いです。ただ、実は賢い無知君の突っ込みは聴いてるだけだと理屈が理解できません。自分頭悪いので。。親父のまとめコメントは抽象的なのでだいたい理解できる。

Audibleで聴いていると吹き出してしまうことがある。自分で読むときは流してしまうけど、きっちり読み上げられるからその大げささが面白いのか。。


*2
*3

Audibleのポータルサイト (Amazonポータルとはドメインが違う・・)
https://www.audible.co.jp/

■追記
体験版で使い始めて、退会を忘れて課金されることがよくあるので、、すぐに退会する。
退会URLは以下より (「退会方法を教えてください」)
https://help.audible.co.jp/s/article/%E9%80%80%E4%BC%9A%E6%96%B9%E6%B3%95%E3%82%92%E6%95%99%E3%81%88%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84

■追記
Audibleは聴いてる時に何をするか?(どういう風に聴くか?)
ながらで聴くのか、メモをしながら真剣に聴くのか・・・慣れないのでどうやったらいいのか分からず。聴いてるだけだと、つかみどころがないので、、目次は印刷して、いまどこを聴いているのか確認するようにしました。

目次プリントの上の白いのは、100均で買ったBlueToothスピーカをばらしてボール紙でホーンをつけたもの。。(BTスピーカは悪くはなかったけど、音がこもるので、ばらしました)

*1:本屋で立ち読みしたが、分厚すぎて買うのをやめた

*2:容易に想像できることではあるが、音声で視聴するのに向くコンテンツと向かないコンテンツがありそうだ。文学作品とかは感情表現が多いので音声の方が伝わりやすいかもしれないが、何かを解説する文書の場合、音声で聴くのはわかりやすいのだろうか。。啓蒙本は音声の方が熱意が伝わっていいかも。

*3:サイトアクセスしていると途中からURLが変わるのだけど、調べてみたら、Audibleのサービスはアマゾンがオーディオブック大手のAudible社を買収して始まったサービスの様だ。画面デザインとAmazon本体とAudibleで少し違っている。

CircuitPythonでPIOを制御

前回はMicroPythonでPIOを制御したが、CircuitPythonで制御してみる
参考ドキュメントは以下
PIOイントロダクション
Overview | An Introduction to RP2040 PIO with CircuitPython | Adafruit Learning System
PIOアセンブラのドキュメント
Introduction — Adafruit PIOASM Library 1.0 documentation
rp2pio Moduleのドキュメント
rp2pio – Hardware interface to RP2 series’ programmable IO (PIO) peripheral. — Adafruit CircuitPython 7.0.0-alpha.1 documentation
GitHub上のソース一式
GitHub - adafruit/Adafruit_CircuitPython_PIOASM: Simple assembler to convert pioasm to bytes

PIOを使いたかったらCircuitPython 6.2.0-beta.3 以降を使えとある。

6.2.0の正式版が出ているのでそれを使う(2021/4/1)
Pico Download

CircutPythonでPIO制御する上での制約

  • ステートマシンは一種類しか使えないようである
  • IRQが使えない。だから、、Python側からはポーリングで待ち受けして、PIOのFIFOに取りに行く必要あり

■メモ
SWDの解説
How to Program and Debug Raspberry Pi Pico with SWD?

$ ls -lF
total 25
-rw-r--r-- 1 sumi なし 10323 Mar 13 19:25 adafruit_bme280.mpy
-rw-r--r-- 1 sumi なし  8786 Mar 13 19:25 adafruit_framebuf.mpy
-rw-r--r-- 1 sumi なし   609 Mar 13 19:25 adafruit_ili9341.mpy
drwxr-xr-x 1 sumi なし     0 Mar 13 19:25 adafruit_imageload/
drwxr-xr-x 1 sumi なし     0 Mar 21 16:31 adafruit_motor/
-rw-r--r-- 1 sumi なし  3415 Mar 13 19:25 adafruit_pioasm.mpy
drwxr-xr-x 1 sumi なし     0 Mar 13 19:55 adafruit_rgb_display/

Pythonのasyncioを理解。。取り組み中

JSのasync/awaitにおいて、背景にあるのはPromiseであると理解。JSのPromiseは非常に深い概念のようでさっくり読めるレベルではない。そこで、、「callback実装の苦闘の末、シンプルに表現できるasync/awaitが発明されたのだ」という理解にとどめて・・、改めてPythonのドキュメントを読んだ。同時実行されるtaskのサンプルを作った。

#!/usr/bin/python3

import asyncio
import time

async def greet(id,t):
   print(f"[T_{id}_1] vvvvv  hello  vvvvv")
   print(f"[T_{id}_2] wait({t}) ")
   await asyncio.sleep(0)
   print(f"[T_{id}_3] exit wait")
   print(f"[T_{id}_4] AAAA  bye  AAAA")
   print(f"[T_{id}_5] vvvvv  hello  vvvvv")
   print(f"[T_{id}_6] wait({t}) ")
   await asyncio.sleep(0)
   print(f"[T_{id}_7] exit wait")
   print(f"[T_{id}_8] AAAA  bye  AAAA")


async def main():

   print("create task_m")
   task_m = asyncio.create_task(greet('m',0))
   print("create task_n")
   task_n = asyncio.create_task(greet('n',0))

   print("[M1] sleep(0)")
   await asyncio.sleep(0)
   print("[M2] sleep(0)")
   await asyncio.sleep(0)
   print("[M3] sleep(0)")
   await asyncio.sleep(0)
   print("exit of main")


M = main()
asyncio.run(M)

上記ソースを実行すると以下の出力となる

$ ./test3.py
create task_m
create task_n
[M1] sleep(0)
[T_m_1] vvvvv  hello  vvvvv
[T_m_2] wait(0)
[T_n_1] vvvvv  hello  vvvvv
[T_n_2] wait(0)
[M2] sleep(0)
[T_m_3] exit wait
[T_m_4] AAAA  bye  AAAA
[T_m_5] vvvvv  hello  vvvvv
[T_m_6] wait(0)
[T_n_3] exit wait
[T_n_4] AAAA  bye  AAAA
[T_n_5] vvvvv  hello  vvvvv
[T_n_6] wait(0)
[M3] sleep(0)
[T_m_7] exit wait
[T_m_8] AAAA  bye  AAAA
[T_n_7] exit wait
[T_n_8] AAAA  bye  AAAA
exit of main

どう処理が流れているのかわかりにくいので、、ソース行単位にマークしてみたのが以下

await asyncio.sleep(0)に出くわした時に実行行の切り替わりが発生しているのが分かる。

他のサンプルも含めて理解したのは以下(正しいかどうかは不明)

  1. asyncioの動作はシングルスレッドであり、マルチスレッドではない(スケジューラもいない)
  2. タスクを生成しても勝手には走らない(プリエンプティブな動作はしない)、明示的なタスク切り替えが必要(await文)
  3. メインループ、タスク間は勝手には切り替わらない。切り替わりのきっかけは実行中のスレッド(タスク?)でのawait文の出現か、他のタスクの終了

await文の理解(理系とは思えない情緒的な表現ですが)

  • 自分は今から項(引数というのか)の処理待ちに入ります。非同期処理として待つ*1ので、Pythonの実行を譲ります。他のタスクで処理待ちの人がいたらそっちを実行してね
  • 自分の次の行は、項(引数というのか)の処理が終わってから(処理待ちが完了後)実行します。ただし・・・↓↓↓
  • sleep等の実行が完了してもタスク切替が行われるまでは続く行を実行しません(処理待ちが完了しても勝手には実行再開しません)

タスクと言っていいのか、スレッドと言うべきか、実行行と言うべきなのか。。

■おまけ
await文では指定した項がタスクの場合、await文で指定されたタスクに実行が移るとは限らない。別のタスクに移る場合もある。

#!/usr/bin/python3

import asyncio
import time

async def task1():
   print("T1_1 start")
   print("T1_2 sleep")
   await asyncio.sleep(0)
   print("T1_3 end")


async def task2():
   print("T2_1 start")
   #while True:
   for _ in range(3):
       print("T2_2  loop....")
       time.sleep(1)     # not switch other task
   print("T2_3 sleep")
   await asyncio.sleep(0)
   print("T2_4 end")

async def main():

   print("M_1 create t1")
   t1 = asyncio.create_task(task1())
   print("M_2 sleep")
   await asyncio.sleep(0)    # switch task1
   print("M_3 create t2")
   t2 = asyncio.create_task(task2())    # switch from task1
   print("--------- all tasks ------")
   print(asyncio.all_tasks())
   print("M_4 await t2")
   await t2               # not switch to task2
   print("M_5 await t1")
   await t1               # switch to task2
   print("M_5 end")

asyncio.run(main())

上記サンプルの場合、 await t2の行で、 タスクt2の実行が再開されて、タスクの実行が終わるまで待つ・・・と解釈してしまいますが、、await t2は t2が終わるまで非同期で待つという意味で、実行をt2に切り替えてt2が終わるのを待つという仕様ではないようです。
↓↓↓そのように判断した根拠(以下の実行結果より)↓↓↓

$ ./test4.py
M_1 create t1
M_2 sleep
T1_1 start
T1_2 sleep
M_3 create t2
--------- all tasks ------
{<Task pending name='Task-1' coro=<main() running at ./test4.py:32> cb=[_run_until_complete_cb() at /usr/lib/python3.8/asyncio/base_events.py:184]>, 
<Task pending name='Task-3' coro=<task2() running at ./test4.py:13>>, 
<Task pending name='Task-2' coro=<task1() running at ./test4.py:9>>}
M_4 await t2          #<<<< await t2としているのに、
T1_3 end                # 制御はt1に移った。
T2_1 start
T2_2  loop....
T2_2  loop....
T2_2  loop....
T2_3 sleep
T2_4 end
M_5 await t1
M_5 end

■追記(2023/8/15)
改めて読み直して、、非同期プログラミングはシングルスレッドなのだから、タスクという言葉を使ってはいかんのではないか。実行単位はスレッドというべきではないか。

*1:非同期で待つ・・・CPUをつかんだ状態では待たない。CPUの実行を手放して待つので、待っている間は他の実行待ちになっている処理を再開して実行してもらえれば良い

pythonのasyncioが全く分からない

普段、非同期処理は必要ないのだけど、、たまたまasyncioに出くわして、ちょっとPythonのドキュメントを読んでみたがどう動くのか全く分からない(実行順番が想像できない)。
以下のサイト等、がっつり説明しているサイトで勉強しないとだめなのかも。

Async IO in Python: A Complete Walkthrough – Real Python

なぜこれほど分からないのか考えた。自分は非同期実装に関するプログラミングモデルが頭の中にない。だから類推がきかない。pythonのasyncioの説明も非同期実装のチュートリアルではなくて、Pythonではこう実装していますという説明だろう。土台となる基礎知識がない中で、動作だけ説明されてもなぜその式や関数があるのか、どう使うのか?何がうれしいのかまで理解が及ばない。非同期実装が一番頻繁に使われているのはJavaScriptと思えて、JSはユーザも多くて解説記事も多いだろう。だから、、まずJSで非同期実装について基礎を学んでから、Pythonの実装を学ぶべきではないかと思った。


Pythonのドキュメント
コルーチンと Task — Python 3.9.4 ドキュメント

テストプログラム

import asyncio
import time

async def d():
     print('D1')
     await asyncio.sleep(0)
     print('D2')
     await asyncio.sleep(1)
     print('D3')

async def e():
     print('E1')
     await asyncio.sleep(0)
     print('E2')
     await asyncio.sleep(4)
     print('E3')


async def hoge():
   print("L1")
   task1 = asyncio.create_task(d())
   print("L2")
   task2 = asyncio.create_task(e())
   print("L3")
   await task1
   print("L4")
   await task2
   print("L5")

print("start")
asyncio.run(hoge())
print("bye")

実行結果

$ python3 test0.py
start
L1
L2
L3
D1
E1
D2
E2
D3
L4
E3
L5
bye

テストプログラム

import asyncio
import time

async def d():
     print('D1')
     await asyncio.sleep(0)
     print('D2')
     await asyncio.sleep(1)
     print('D3')

async def e():
     print('E1')
     await asyncio.sleep(0)
     print('E2')
     await asyncio.sleep(4)
     print('E3')


async def hoge():
   print("L1")
   task1 = asyncio.create_task(d())
   print("L2")
   task2 = asyncio.create_task(e())
   print("L3")


print("start")
asyncio.run(hoge())
print("bye")

実行結果

$ python3 test1.py
start
L1
L2
L3
D1 
E1
bye

漠然とした理解
asyncioは、、待ってくれる式や関数?に出くわすまで実行されない。。 awaitは待ってくれる式。。待ってくれる状態になって初めてawaitioで定義された関数は動き始める。。。また、関数内でsleepすると、他の待っているawaitioが動き始める。だけど、、task として生成すると、待たずに動き始める。awaitに出くわすと、自分の実行は一旦そこで止めて、他で待っているawaitableの実行を開始する。。うーん。。

■ご参考:がっつり読みたいJS解説記事

async/await 入門(JavaScript) - Qiita

(↓ちょっと宣伝が多いですが。。)
【JavaScript入門】5分で理解!async / awaitの使い方と非同期処理の書き方 | 侍エンジニアブログ

await - JavaScript | MDN

JavaScriptは如何にしてAsync/Awaitを獲得したのか Qiita版 - Qiita

JavaScript Promiseの本

複雑で長すぎてとても読む力はないけど、、コールバックから紆余曲折を経て、async/awaitが発明され、async/awaitを使うことでコードがすっきり書けることは分かった。
JavaScriptは如何にしてAsync/Awaitを獲得したのか Qiita版 - Qiita

■追記
分かったこと:
async/awaitは本来シングルスレッドで実行しているプログラムに非同期的な動作を簡潔に表現するために作り出された機構である

参照先文献(JS)より引用

Async Functionとawait式を使うことで非同期処理をまるで同期処理のように書けます。
重要なこととしてAsync FunctionはPromiseの上に作られた構文です。 そのためAsync Functionを理解するには、Promiseを理解する必要があることに注意してください。

前半の文書は、後半はよくわかる。どう見ても同期処理ぢゃないか!と思っていたから。async/awaitを使うことで、非同期処理を同期処理のように書けるということか。一見同期処理の裏側にある非同期というのが見えなくなっている。自分の視力では見えない。。

Swift NWConnectionGroupのサンプル

MulticastでEchonetLite (GET / 自ノードインスタンスリスト) 投げて機器からUnicast(自ノードインスタンスリスト)で戻ってくるパケットを受け取るサンプル。このソースでは受信できない、(Multicastで受け取る実装になっているから)

import Foundation
import Network

var EL_HEADER:[UInt8] = [0x10, 0x81]
var EL_TID:[UInt8] = [0x00, 0x00]
var EOJ_CONTROLLER:[UInt8] = [0x05, 0xff, 0x01]
var EOJ_NODEPROFILE:[UInt8] = [0x0e, 0xf0, 0x01]

let ESV_GETI:UInt8 = 0x60
let ESV_GETC:UInt8 = 0x61
let ESV_GET:UInt8 = 0x62
let ESV_INFREQ:UInt8 = 0x63
let ESV_SETGET:UInt8 = 0x6E
let EPC_INSLSTNTFPROP:UInt8 = 0xD5
let EPC_INSLIST:UInt8 = 0xD6
let EPC_STATE:UInt8 = 0x80

func device_scan(){

guard let multicast = try? NWMulticastGroup(for:
    [ .hostPort(host: "224.0.23.0", port: 3610)], disableUnicast: false)
    else { fatalError("error in Muticast") }

let group = NWConnectionGroup(with: multicast, using: .udp)

group.setReceiveHandler(maximumMessageSize: 1024, rejectOversizedMessages: true) { (message, content, isComplete) in
    print("Received message from \(String(describing: message.remoteEndpoint))")
    //let message = String(data: content, encoding: .utf8)
    //let message = Data(content, encoding: .utf8)
    print(content)
    //let sendContent = Data("ack".utf8)
    //message.reply(content: sendContent)
}
group.stateUpdateHandler = { (newState) in
    print("Group entered state \(String(describing: newState))")
    switch newState {
    case .ready:
        print("ready")

        var msg:[UInt8] = EL_HEADER + EL_TID + EOJ_CONTROLLER + EOJ_NODEPROFILE
        //msg.append(contentsOf:[ESV_INFREQ, 0x01, EPC_INSLSTNTFPROP, 0x00])
        msg.append(contentsOf:[ESV_GET, 0x01, EPC_INSLIST, 0x00])
        let groupSendContent = Data(msg)  // .data(using: .utf8)

        print("send...UDP")
        group.send(content: groupSendContent) { (error)  in
            print("Send complete with error \(String(describing: error))")
        }
    case .waiting(let error):
        print("waiting")
        print(error)
    case .setup:
        print("setup")
    case .cancelled:
        print("cancelled")
    case .failed:
        print("failed")
    //case .preparing:
    //    print("preparing")
    default:
        print("default")
    }
}
let queue = DispatchQueue(label: "ExampleNetwork")
//print(group.isUnicastDisabled)
group.start(queue: queue)
//group.start(queue: .main)
sleep(10)
group.cancel()
sleep(5)
print("bye")
}

device_scan()