当我在玩 MakeCode 时候,无意间发现 EV3 的显示屏可以显示自定义图像并且支持发出声音……
Bad Apple!!
首先先下载 Bad Apple!! 视频并利用 ffmpeg 转换成 png 图片 (178*128 10fps)
ffmpeg -i bad_apple.mp4 -vf scale=178:128 -r 10 ba_%5d.png
第一步是想办法将图片放置到 EV3 里。由于 MakeCode 似乎不支持上传文件,并且对代码长度有最大限制,于是就必须想出一种压缩方式。
这里我选用的压缩方式是将每帧转换成黑白图片,并且转换成 u8 的数组 a,数组a[2n]
代表接下来黑色像素点数量,a[2n+1]
代表白色像素点数量。最后再用base64加密放入 MakeCode 代码中(其实应该也可以直接创建数字数组的,可能没必要用 base64)。
压缩转换代码如下:
import base64
from PIL import Image
def compress(filename):
res=[]
img = Image.open(filename) .convert('L').point(lambda x : 255 if x > 200 else 0, mode='1')
pixels = list(img.getdata())
cnt=0
lst_color=0 # start from black
for p in pixels:
if lst_color==p:
if cnt==255: # avoid overflow
res.append(cnt)
res.append(0)
cnt=0
cnt+=1
else:
lst_color=p
res.append(cnt)
cnt=1
res.append(cnt)
return base64.b64encode(bytearray(res)).decode()
之后就简单了,直接在 EV3 上编码图片并显示即可:
data.forEach(function (f: string) {
let myImage = image.create(178, 128);
let decoded = new Base64().decode(f);
let is_black = true; // start from black
let x = 0; let y = 0;
for (let i = 0; i < decoded.length; ++i) {
let n = decoded.charCodeAt(i);
for (let j = 0; j < n; ++j) {
if (is_black) myImage.setPixel(x, y, 1);
x++;
if (x == 178) { x = 0; y++; }
}
is_black = !is_black;
}
brick.showImage(myImage);
});
吐槽一下,MakeCode 用的是阉割版的 TypeScript,很多 JavaScript 里的东西都不能用,导致 Base64 都要造轮子。 (此处应该有一个视频,但是由于太懒了就没弄)
马里奥
做完 Bad Apple!! 之后发现 EV3 竟然支持 playTone(frequency, duration)
,于是写了个简单的脚本转换 MIDI 文件成 MakeCode 代码。
import sys
# From https://gist.github.com/YuxiUx/ef84328d95b10d0fcbf537de77b936cd
def noteToFreq(note):
a = 440 #frequency of A (coomon value is 440Hz)
return (a / 32) * (2 ** ((note - 9) / 12))
# http://valentin.dasdeck.com/midi/mid2txt.php
SPEED=1.04166666666667 #60000 / (BPM * PPQ)
#music.playTone(659, 1013 - 512);
#loops.pause(1536 - 1013)
with open("out.txt", "w") as out:
with open(sys.argv[1]) as file:
for line in file:
#0 On ch=1 n=76 v=110
vals=line.split(" ")
if len(vals)!=5:
print("[*] unrecognized line", vals, "skipping...")
continue
time, status, _, note, vol = vals
time=int(time)
if status!="On" or time ==0 or vol==0:continue
out.write("music.playTone(%s, %d);\n"%(noteToFreq(int(note[2:])), time/SPEED));
print("[*] ok!")