Redirecting...
0%

listen2me

使用mml作曲。

download

此分支为Windows版本。

listen2me是为Dice!编写的Lua脚本,用于mml(Music Macro Language)作曲试听。

如果在使用过程中遇到了困难,欢迎到原仓库提交issue;如果在使用过程中修改出更有意思的玩法,也欢迎到原仓库提交PR

1
2
3
4
5
6
7
8
9
10
11
12
{
"mod":"listen2me",
"author":"简律纯",
"ver":"2.1.0",
"dice_build":612,
"brief":"使用mml作曲",
"comment":"",
"helpdoc":{
"listen2me":"【listen2me[Windows]】\n使用mml语言进行作曲\nhttps://github.com/A2C29K9/listen2me",
"loopstraining":"基于listen2me的听音训练娱乐模块"
}
}
  • Windows 版本
  • Linux 版本

安装

  • Dice版本2.6.5beta12(624+)以上安装方法:

    1. ./DiceQQ/conf/mod/source.list文件内(没有mod文件夹和这文件就新建)输入 https://ssjskfjdj.netlify.app/Module/
    2. 使用 .system load命令重载bot,这样做的目的是为了让步骤1里的远程地址生效。
    3. 对bot发送 .mod get listen2me命令,等待安装。
    4. 回到第二步,这样做的目的是为了让mod被加载。
    5. Enjoy Your Self!
  • Dice版本2.6.4b(612+)以上安装方法:

    1. 浏览器访问 https://github.com/ssJSKFJDJ/listen2me并点击绿色按钮 Code下的 Download Zip按钮下载仓库压缩包。
    2. 解压压缩包,将里面的文件和文件夹全部丢进 ./DiceQQ/mod/文件夹内。
    3. 使用 .system load命令重载。
    4. Enjoy Your Self!

配置

使用功能模块前先根据注释内容修改合适的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
--------------------settings---------------------
Settings = {
_FRAMWORK = "Gocq", -- "Mirai",
-- 框架名称,必填,可选参数:'Mirai'或'Gocq'(默认).

_ONEFILE = "xx", -- os.date("%A"),
-- 是否将每次的乐谱记录在同一个文件内.

_WARNING = 18,
-- 音频文件过多报警上限,未填时默认18

_AUTOCLR = 21,
-- 音频文件自动清理,未填时默认21

_SUBNAME = ".mp3", -- ".wav",
-- 规定输出格式,填写mp3时需要安装ffmpeg.

_UPLOAD = false, -- false,
-- 是否在发出语音同时上传mid文件(仅限Gocq).
-- 对应下参数_APIPORT.

_APIPORT = 15700 -- nil
-- Gocq 本地 API 端口,填nil时将自动搜寻,若报错请自行填入.
}

指令

一般指令格式:l2m>[opt=clr|mml|...]

  • 清理缓存
    成功执行一次mml命令后会在 ./project/生成这几样东西:

    1. fileName.mid
    2. fileName.mml
    3. fileName.mp3|wav

    这些就是所谓的缓存,一次生成三个,因此才有了 _WARNING_AUTOCLR配置——但要注意的是,这些配置的存在只是为了那些不会主动的清理的人罢了,如果你比较勤快那么我想除了添加 _ONEFILE配置项外,你一定会经常使用 l2m>clr指令去清理这些缓存的。

  • 生成曲谱
    指令格式:l2m>[mml],由于mml语法过于繁琐因此在这里不一一赘述,只是做几个示例,日后会写专门的文档系统介绍此功能模块的mml语法。

    1. 单轨示例
    1
    l2m>A cdefgab
    1. 多轨示例
    1
    2
    3
    4
    l2m>
    A l8d+>d+d+d+d4<a+4>cc<a+g+a+2d+>d+d+d+d4.<a+b+2g+4a+4d+>d+d+fd4<a+4>cl16d+f8.d+<a+4.&a+gfl8d+>d+d+gfd+dl16<a+>d+4.&d+cdd+8dd+8.<a+8>fgf2gg8.fd+f8cd+4&d+<a+8>gg8.fd+f8d+<a+4.&a+>gg8.fd+f8cd+4d+d+8dd8.cc8.<a+a+8.>cd+g+8.g8.fd+f8cd+4&d+<a+8>dd8d+8f<a+8ga+8>f8d+d+8.<a+a+8g+gg+8>d+d+2<a+a+8g+gg+8>ff4.&fd+8d+8df8d+2.

    B /*M 0 */ <c8g8>d+4<<g8>d8a+4/*M 1 */ <g+8>d+8<a+4>d+4d4/*M 2 */ c8g8>d+4<<g8>d8a+4/*M 3 */ <g+8>d+8>c8<d+8<g+8>d+8<a+8b8/*M 4 */ >c8g8>d+8<g8<g8>d8a+8d8/*M 5 */ <g+8>d+8<a+4>d+8a+8d4/*M 6 */ c8g8>d+8<g8<g8>d8a+8d8/*M 7 */ <g+8>d+8>c8.<<a+16b4g+4/*M 8 */ a+8>f8a+4<<a+2/*M 9 */ >g+8>d+8>c8<d+8<g+8>d8a+8d8/*M 10 */ <g8>d+8a+8d+8c8g8>d8d+8/*M 11 */ <<f8>g8>d+8<g+8<a+8>f8>d8<a+8/*M 12 */ <d+8a+8>d+8<a+8>c+8<a+8>c+8<<a+8/*M 13 */ >g+8>d+8>c8<d+8<g+8>d8a+8d8/*M 14 */ <g8>d8.<g8.>c4<d8d+8/*M 15 */ f8>c8g+8c8<g8>d+8a+8d+8/*M 16 */ <g+8>d+8>c8<d+16.<<a+32o4d2/*M 17 */ <<d+16a+16>g16g+16a+8.<d+4.&d+16d+8

音源

Musescore 提供的三个常见音色库

  • FluidR3_GM.sf2 (未压缩大小 141 MB)
    下载 Fluid-soundfont.tar.gz
  • GeneralUser_GS_1.44-MuseScore.sf2 (未压缩大小 29.8 MB)
    下载 GeneralUser_GS_1.44-MuseScore.zip (承蒙 S. Christian Collins 而提供)
  • TimGM6mb.sf2 (未压缩大小 5.7 MB)
    下载 modified TimGM6mb.sf2 (承蒙 Tim Brechbill 而提供)

FluidSynth 提供的音源下载网站

  • Polyphone Soundfont Collection - A collection of Soundfonts on the Polyphone website
  • Hammersound - A nice resource for downloading free SoundFont instrument files.
  • Magic Sound Font, version 2.0 (68 MB)
  • Arachno SoundFont, version 1.0 (148MB)
  • TimGM6mb (6 MB)
  • MuseScore_General.sf2 (208 MB)
  • Timbres Of Heaven GM_GS_XG_SFX V 3.4 (246 MB)
  • Sonatina Symphonic Orchestra (503 MB uncompressed)
  • Aegean Symphonic Orchestra v2.5 universal (350 MB)
  • Salamander C5 Light (25 MB)

参考

  • http://hpc.jp/~mml2mid
  • https://man.archlinux.org/man/timidity.cfg.5
  • https://man.archlinux.org/man/community/timidity++/timidity.1.en

日志

此日志写的比工程文件要晚那么一点。


2022年11月20日 开了一个新的坑——听音练习。
新增
1.loopstraining新增听音练习娱乐模块。
改动
1.mml2mid.lua将所有的局部变量名称首字母大写。
2022年10月29日 发现了几个bug,更新了readme。
issue
1.mml2mid.luatimidity线程问题,速度过快会导致timidity错误。
新增
1.readme.md新增指令介绍。
2022年10月13日 完善Gocq上传群文件机制。
新增
1.mml2mid.lua_UPLOAD_APIPORT完善,预期功能已实现。
改动
1.mml2mid.lua原本注释的_WARNING配置项现已恢复原有功能。
2.mml2mid.lua配置项_AUTOCLR默认上限提高。
2022年10月11日 新增配置项。
新增
1.mml2mid.lua新增_UPLOAD_APIPORT配置,详细使用说明见脚本。
2022年10月9日 实现了timidity.cfg的自动写入。
新增
1. mml2mid.lua 新增了对timidity音源的检测。
2.mml2mid.lua 新增_FRAMWORK配置项,判断框架类型。
3.mml2mid.lua 新增mml语法报错检测和输出(如果你看得懂那会很有帮助就是了)。
修复
1. 修复了因timidity.cfg参数dir固定而无法输出有声音的音频问题。
2022年10月8日 初步框架版本编写完毕。
新增
1. mml2mid.lua 新增一些配置项。
2. 新增了对 timidity.cfg 的查询功能。
删减
1. 改动了部分语法。
2022年10月2日 准备套壳,已经实现mml转mid,mid转wav,下一阶段将会编写脚本使其脚本化。
新增
1. 添加timidity,放弃了原本直接发送midi序列的方式(这样Go-cqhttp会发不出语音),在发送语音前会对*.mid文件进行转码再发送。
修复
1. 修复了一些bug。

2022年11月20日02点00分

Lua原生函数load的继承性

这篇帖子是没有学习目标的,并且更适合有Lua编写基础的人观看。
今天就是想不明不白的开始。
关于 load,如果看过【指令脚本/抛砖引玉】Load: 在聊天窗口使用lua 那么你或许会对它熟悉一些,此函数是 lua 5.1就有的东西,作用是加载一个数据块。下面是现今的官方5.2 manual给出的关于它的介绍:

1
2
3
4
5
6
7
8
9
10
11
12
load (ld [, source [, mode [, env]]])
Loads a chunk.

If is a string, the chunk is this string. If is a function, calls it repeatedly to get the chunk pieces. Each call to must return a string that concatenates with previous results. A return of an empty string, nil, or no value signals the end of the chunk. ldldloadld

If there are no syntactic errors, returns the compiled chunk as a function; otherwise, returns nil plus the error message.

If the resulting function has upvalues, the first upvalue is set to the value of , if that parameter is given, or to the value of the global environment. (When you load a main chunk, the resulting function will always have exactly one upvalue, the variable (see §2.2). When you load a binary chunk created from a function (see string.dump), the resulting function can have arbitrary upvalues.) env_ENV

source is used as the source of the chunk for error messages and debug information (see §4.9). When absent, it defaults to , if is a string, or to "" otherwise. ldld=(load)

The string controls whether the chunk can be text or binary (that is, a precompiled chunk). It may be the string "" (only binary chunks), "" (only text chunks), or "" (both binary and text). The default is "".modebtbtbt

什么意思?

坦白来说,就是从字符串或者函数中加载一个代码块为方法并返回。——应该不难理解吧?

但你需要明白以下几点:

  1. load()返回的是函数,需要调用的话,还需要加括号。
  2. 5.1.x版本 load方法为 load(func[,chunkname])从函数中加载,loadstring(str[,chunkname])从字符串中加载
  3. 5.3.x版本为 load (chunk [, chunkname [, mode [, env]]])
    chunk为函数或字符串
    mode为加载模式:”t”文本样式,”b”二进制样式,”bt”二进制和文本模式.
    env代码块需要的参数

你可以这样对自己的骰娘做个测试(我称此方法为简式测试法)

  1. DiceQQ\plugin\下新建 test.lua
  2. DiceQQ\plugin\test.lua内写上如下代码:
test.lua
1
2
msg_order={["@"]="main"}
local main = function(msg) return load(msg.fromMsg:sub(2))() end
  1. 对骰娘发送 .system load命令重载。
  2. 对骰娘发送 @return msg.fromQQ,你将会收到自己的QQ。

其实到这里就是【指令脚本/抛砖引玉】Load: 在聊天窗口使用lua 的全部内容了。下面进入正题。
因为昨天,哦不,今天凌晨喝的有点多,又很奇怪的是我醒的特别早,我大概五点就醒了,然后开始敲代码,也许是酒精的作用吧,我对krypton进行了如下无厘头测试:

1
2
3
4
tbl={}
tbl['第1层']="tbl[\"tbl['第2层']\"]"
tbl["tbl['第2层']"]="第3层"
return load("return "..tbl['第1层'])()

猜猜返回了什么?

我又进行了推导:

1
2
3
4
5
tbl={}
tbl['第1层']="tbl[\"tbl['第2层']\"]"
tbl["tbl['第2层']"]="tbl[\"tbl['第3层']\"]"
tbl["tbl[\"tbl['第3层']\"]"]="第4层"
return load("return "..tbl['第1层'])()

又返回了什么?
希望能有人好好运用吧。再不行就只能我自己写了()

教程结束。

secondary

Colorful-Error-Repo

简单的自定义报错回复模板.

Colorful-Error-Repo:来点不一样的报错吧

使用《署名—禁止演绎 4.0 协议国际版》(CC BY-ND 4.0)进行授权。
https://creativecommons.org/licenses/by-nd/4.0/legalcode.zh-Hans

续之前的弦:骰娘自检/判断报错并自定义回复


一、基本信息

  • 作者: 简律纯
  • 联系方式:qq:a2c29k9
  • 版本:v1.0
  • 更新日期:暂无
  • 关键词:暂无
  • 许可协议:CC BY-ND 4.0

二 、需求

  • 更多的lua报错类型列表
  • 更多时间

三、准备

1.暂时用了手册内色子整理的常见报错信息说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
module '%s' not found:
no field package.preload['%s']
no file
-- 没有把被引用的lua或dll文件放在指定位置(多见于require与loadLua)
-- 解决方式:把所需文件放入Dice存档目录/plugin/或Diceki/lua/,dll文件或require对象必须置于后者
attempt to call a nil value (global '%s')
-- 将空变量%s用作函数(global表示被调用的是全局变量,local表示本地变量,method表示索引方法)
attempt to index a nil value (global '%s')
-- 对空变量%s使用索引(只有table等结构可以索引,形如msg.fromMsg)
attempt to concatenate a nil value (local '%s')
-- 使用..连接字符串时连接了一个空变量%s
bad argument #1 to '%s' (string expected, got nil)
-- 函数%s的第1个参数类型错误,要求类型为string,但实际传入的参数为nil。特别地,got nil表示输入参数为nil或缺少参数
value expected
-- 要求参数,但没有传入
attempt to perform arithmetic on a nil value (global '%s')
-- 将一个空变量%s当做数字表示
bad argument #5 to 'format'(number has no integer representation)
-- 函数format的第5个参数类型错误,要求是整数,但传入是小数,或者是其余类型不能化为整数
'}' expected (to close '{' at line 169) near '%s'
-- 脚本第169行的左花括号缺少配对的右花括号。此错误也可以由表格内缺少逗号分隔、括号外的中文等原因造成
'end' expected (to close 'function' at line 240) near <eof>
-- 脚本第240行的function缺少收尾的end,<eof>表示文件结束(找到文件末也没找到)
'then' expected near 'end'
-- if then end逻辑结构缺少then
unexpected symbol near '%s'
-- 符号%s边有无法识读的符号,比如中文字符
attempt to get length of a nil value (local 'tab')
-- 对空变量tab作取长度运算(#)
attempt to add a 'string' with a 'string'
-- 对(不能化为数字的)字符串用加法'+'(字符串只能用连接'..')
attempt to compare number with string
-- 对数字和(不能化为数字的)字符串用比较运算符
error loading module '%s' from file
-- 使用require "%s"时加载出错
no visible label '%s' for <goto> at line
-- 在循环结构中跳转不存在的节点
invalid option '%s'
-- 传入的参数不是string或不在给定的字符串列表中

四、引用清单

  1. https://v2docs.kokona.tech/zh/latest/Develop_Manual.html#id22

lua-colors

用lua解释色彩原理.

颜色通常在软件中通过其在RGB(红-绿-蓝)颜色空间中的坐标进行编码。 不幸的是,RGB色彩空间非常不直观,另外某些颜色协调的原则在RGB中不容易表达出来。 HSL(色相-饱和度-亮度)色彩空间解决了这两个问题。这个库允许你在HSL色彩空间中处理颜色,计算协调的图案,然后转换为RGB。 (你可以在 worqx.com 看到更多关于色彩协调的相关内容)

HSL 色彩空间

颜色在HSL中由三个值编码:色相,饱和度和亮度。

亮度(Lightness) 正好与颜色的亮暗相反。 白色亮度为1,黑色亮度为0。 其他颜色介于它们之间。

饱和度(Saturation) 代表颜色的强度,它显示颜色与灰色的距离。 以下是不同饱和度和亮度的红色渐变:

 
Lightness
   0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
Saturation 0.0                      
0.1                      
0.2                      
0.3                      
0.4                      
0.5                      
0.6                      
0.7                      
0.8                      
0.9                      
1.0                      

色相/色调(Hue) 是颜色的“color”:它让“绿色”与“红色”不同。色相 也可以表示为介于 0 和 1 之间的数字,但此库使用从 0 到 360 的值。
与亮度和饱和度不同,hue __loops__:色相360实际上与 0(红色)相同。

Saturation
   1.0 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0.0
Hue 0                      
10                      
20                      
30                      
40                      
50                      
60                      
70                      
80                      
90                      
100                      
110                      
120                      
130                      
140                      
150                      
160                      
170                      
180                      
190                      
200                      
210                      
220                      
230                      
240                      
250                      
260                      
270                      
280                      
290                      
300                      
310                      
320                      
330                      
340                      
350                      
360                      

使用颜色进行 HSL 计算

在HSL空间中创建颜色并将其转换为RGB。

1
2
3
4
local colors = require("colors")
c = colors.new(130, .8, 0.3) -- 绿色,相当饱和,有点暗
-- =tostring(c)
-- #0f8923
 

你也可以通过它的RGB代码创建这些颜色:

1
2
3
4
local colors = require("colors")
c = colors.new("#0f8923")
-- =tostring(c)
-- #0f8923

当强制转换为字符串时,颜色将转换为其 RGB 形式:

1
2
=c
-- #0f8923
 

查看 HSL :

1
2
print(c.H, c.S, c.L)
-- 130 0.8 0.3

改变饱和度:

1
2
=c:desaturate_by(.5) -- 将饱和度设置为饱和度*.5
-- #2d6b38
 
1
2
=c:desaturate_to(.5) -- 将饱和度设置为 .5
-- #267233
 

改变亮度:

1
2
=c:lighten_by(.5) -- 将亮度设置为亮度*.5
-- #14b72f
 
1
2
=c:lighten_to(.5) -- 将亮度设置为 .5
-- #19e53b
 

改变色相:

1
2
=c:hue_offset(180) -- 色调偏移 180
-- #890f75
 

构建配色方案

要构建配色方案,我们通常从颜色开始,选择一种或多种匹配的颜色,然后从中获取阴影和色调。你可以在[worqx.com](http://www.worqx.com/color/combinations.htm)找到有关颜色组合的一些东西。

对于 单色 配色方案,我们只使用一开始的颜色以及它的色相和阴影:

1
2
3
4
5
6
7
tints = c:tints(5) -- 生成色相
for i,t in ipairs(tints) do print(t) end
-- #16c934
-- #3ee95a
-- #7ef091
-- #bef7c8
-- #ffffff
         
1
2
3
4
5
6
7
shades = c:shades(5) -- 生成阴影
for i,s in ipairs(shades) do print(s) end
-- #0c6e1c
-- #095215
-- #06370e
-- #031b07
-- #000000
         

对于懒人方案,我们可以轻松得出懒人想要的颜色及其色调和阴影:

1
2
3
4
5
6
7
ctints = c:complementary():tints(5) -- 制作五种颜色的互补色
for i,t in ipairs(ctints) do print(t) end
-- #c916ac
-- #e93ecd
-- #f07edd
-- #f7beee
-- #fffff
         
         

但是,为了减少对比度,我们可以坚持使用相邻的颜色:例如,起始颜色的 +/- 60度:

1
2
3
4
5
6
7
8
9
10
11
12
13
n1, n2 = c:neighbors(60)  -- 获得上下60个相邻的颜色
for i,t in ipairs(n1:tints()) do print(t) end
-- #16c98e
-- #3ee9b0
-- #7ef0ca
-- #bef7e4
-- #ffffff
for i,t in ipairs(n2:tints()) do print(t) end
-- #52c916
-- #77e93e
-- #a4f07e
-- #d1f7be
-- #ffffff
         
         
         

或者,我们也可以生成一个拆分的互补配色方案:

1
2
3
4
5
6
7
8
9
10
11
12
for i,t in ipairs(c1:tints()) do print(t) end
-- #8e16c9
-- #b03ee9
-- #ca7ef0
-- #e4bef7
-- #ffffff
for i,t in ipairs(c2:tints()) do print(t) end
-- #c91652
-- #e93e77
-- #f07ea4
-- #f7bed1
-- #ffffff
         
         
         

或者三色混合:

1
2
3
4
5
6
7
8
9
10
11
12
13
t1, t2 = c:triadic()
for i,t in ipairs(t1:tints()) do print(t) end
-- #3416c9
-- #5a3ee9
-- #917ef0
-- #c8bef7
-- #ffffff
for i,t in ipairs(t2:tints()) do print(t) end
-- #c93416
-- #e95a3e
-- #f0917e
-- #f7c8be
-- #ffffff
         
         
         

table module

对数组操作函数的拓展,利用这些函数可以更好的进行数据操作。

table-module:更多的数组操作

使用《署名—禁止演绎 4.0 协议国际版》(CC BY-ND 4.0)进行授权。
https://creativecommons.org/licenses/by-nd/4.0/


[ toc ] 更多操作,更多的可能猝死


一、基本信息

  • 二次修改作者: 简律纯
  • 联系方式:qq:a2c29k9
  • 版本:v1.0
  • 更新日期:暂无
  • 关键词:暂无
  • 许可协议:CC BY-ND 4.0

# 函数名 简单介绍
1 table.nums 计算表格包含的字段数量
2 table.keys 返回指定表格中的所有键
3 table.values 返回指定表格中的所有值
4 table.merge 将来源表格中所有键及其值复制到目标表格对象中
5 table.indexof 从表格中查找指定值,返回其索引
6 table.keyof 从表格中查找指定值,返回其 key
7 table.removebyvalue 从表格中删除指定值,返回删除的值的个数
8 table.map 对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容
9 table.walk 对表格中每一个值执行一次指定的函数,但不改变表格内容
10 table.filter 对表格中每一个值执行一次指定的函数
11 table.unique 遍历表格,确保其中的值唯一

二、详细介绍

1.table.nums(t)

1
2
3
4
5
6
7
8
-- 计算表格包含的字段数量
-- @function [parent=#table] nums
-- @param table t 要检查的表格
-- @return integer#integer

-- 计算表格包含的字段数量
-- Lua table 的 "#" 操作只对依次排序的数值下标数组有效,
-- table.nums() 则计算 table 中所有不为 nil 的值的个数。

2.table.keys(hashtable)

1
2
3
4
5
6
7
8
9
-- 返回指定表格中的所有键
-- @function [parent=#table] keys
-- @param table hashtable 要检查的表格
-- @return table#table

-- 返回指定表格中的所有键
local hashtable = {a = 1, b = 2, c = 3}
local keys = table.keys(hashtable)
-- keys = {"a", "b", "c"}

3.table.values(hashtable)

1
2
3
4
5
6
7
8
9
-- 返回指定表格中的所有值
-- @function [parent=#table] values
-- @param table hashtable 要检查的表格
-- @return table#table

-- 返回指定表格中的所有值
local hashtable = {a = 1, b = 2, c = 3}
local values = table.values(hashtable)
-- values = {1, 2, 3}

4.table.merge(dest, src)

1
2
3
4
5
6
7
8
9
10
-- 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值
-- @function [parent=#table] merge
-- @param table dest 目标表格
-- @param table src 来源表格

-- 将来源表格中所有键及其值复制到目标表格对象中,如果存在同名键,则覆盖其值
local dest = {a = 1, b = 2}
local src = {c = 3, d = 4}
table.merge(dest, src)
-- dest = {a = 1, b = 2, c = 3, d = 4}

5.table.indexof(array, value, begin)

1
2
3
4
5
6
7
8
9
10
-- 从表格中查找指定值,返回其索引,如果没找到返回 false
-- @function [parent=#table] indexof
-- @param table array 表格
-- @param mixed value 要查找的值
-- @param integer begin 起始索引值
-- @return integer#integer

-- 从表格中查找指定值,返回其索引,如果没找到返回 false
local array = {"a", "b", "c"}
print(table.indexof(array, "b")) -- 输出 2

6.table.keyof(hashtable, value)

1
2
3
4
5
6
7
8
9
-- 从表格中查找指定值,返回其 key,如果没找到返回 nil
-- @function [parent=#table] keyof
-- @param table hashtable 表格
-- @param mixed value 要查找的值
-- @return string#string 该值对应的 key

-- 从表格中查找指定值,返回其 key,如果没找到返回 nil
local hashtable = {name = "dualface", comp = "chukong"}
print(table.keyof(hashtable, "chukong")) -- 输出 comp

7.table.removebyvalue(array, value, removeall)

1
2
3
4
5
6
7
8
9
10
-- 从表格中删除指定值,返回删除的值的个数
-- @function [parent=#table] removebyvalue
-- @param table array 表格
-- @param mixed value 要删除的值
-- @param boolean removeall 是否删除所有相同的值
-- @return integer#integer

-- 从表格中删除指定值,返回删除的值的个数
local array = {"a", "b", "c", "c"}
print(table.removebyvalue(array, "c", true)) -- 输出 2

8.table.map(t, fn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- 对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容
-- @function [parent=#table] map
-- @param table t 表格
-- @param function fn 函数

-- 对表格中每一个值执行一次指定的函数,并用函数返回值更新表格内容
local t = {name = "dualface", comp = "chukong"}
table.map(t, function(v, k)
-- 在每一个值前后添加括号
return "[" .. v .. "]"
end)

-- 输出修改后的表格内容
for k, v in pairs(t) do
print(k, v)
end
-- 输出
-- name [dualface]
-- comp [chukong]

-- fn 参数指定的函数具有两个参数,并且返回一个值。原型如下:
function map_function(value, key)
return value
end

9.table.walk(t, fn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 对表格中每一个值执行一次指定的函数,但不改变表格内容
-- @function [parent=#table] walk
-- @param table t 表格
-- @param function fn 函数

-- 对表格中每一个值执行一次指定的函数,但不改变表格内容
local t = {name = "dualface", comp = "chukong"}
table.walk(t, function(v, k)
-- 输出每一个值
print(v)
end)

-- fn 参数指定的函数具有两个参数,没有返回值。原型如下:
function map_function(value, key)
end

10.table.filter(t, fn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除
-- @function [parent=#table] filter
-- @param table t 表格
-- @param function fn 函数

-- 对表格中每一个值执行一次指定的函数,如果该函数返回 false,则对应的值会从表格中删除
local t = {name = "dualface", comp = "chukong"}
table.filter(t, function(v, k)
return v ~= "dualface" -- 当值等于 dualface 时过滤掉该值
end)
-- 输出修改后的表格内容
for k, v in pairs(t) do
print(k, v)
end
-- 输出
-- comp chukong

-- fn 参数指定的函数具有两个参数,并且返回一个 boolean 值。原型如下:
function map_function(value, key)
return true or false
end

11.table.unique(array)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 遍历表格,确保其中的值唯一
-- @function [parent=#table] unique
-- @param table array 数组
-- @return table#table 包含所有唯一值的新表格

-- 遍历表格,确保其中的值唯一
local t = {"a", "a", "b", "c"} -- 重复的 a 会被过滤掉
local n = table.unique(t)
for k, v in pairs(n) do
print(v)
end
-- 输出
-- a
-- b
-- c

三、下载

table-module.zip

Midido
简单实现用lua输出midi的库。

使用《署名—非商业性使用—相同方式共享 4.0 协议国际版》(CC BY-NC-SA 4.0)进行授权。
https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.zh-Hans

Midido To-do list 描述 状态
wiki 用法wiki x
Keyboards’ pack 键位包 敬请期待
songs’ e.g. 歌曲工程示例 x

一、基本信息

  • 作者: 简律纯
  • 联系方式:qq:a2c29k9
  • 版本:v1.0
  • 更新日期:暂无
  • 关键词:暂无
  • 许可协议:CC BY-NC-SA 4.0

二、详细介绍

1.简介

最初是一个设想,毕竟我是写音乐的,主要负责作曲编曲,鸽了快一年后很多粉丝不耐烦了,于是我开始整一些或许大学用得上的东西^比如一个bot ,高考那几天突发奇想,我或许可以教粉丝朋友一起写音乐?那就先从midi序列开始吧!(也可以名正言顺咕咕咕,同时压榨劳动力)

2.一些教训

1.之前试过onebot ZBP插件里的timidity,也是手搓midi,但是仍然需要安装ffmpeg和timidity环境,且受到一定的系统环境影响——尽管成功写出了midi文件,但是对于其他那些使用非Windows系统(大家其实更愿意用Macbook)的人(主要是我的作曲家编曲家朋友)来说安装十分困难。
zbp关于midi的插件介绍:

2.于是我开始注意到简化使用指令的重要性,并在此机缘巧合下找到了 AutoPiano
我并不在意它的关于编写输出音乐的任何逻辑或是别的什么,我找到了站长,并向他提出了几个问题——就比如,键盘谱是如何想出来的。
下图为孤勇者唱谱:

3.综上,这未尝不是一个很好的开端,至少我明白了手搓midi的原理,以及一些更切合实际的东西,就比如简化指令。

三、TO-DO LIST

  • 编写用法wiki
    用法非常复杂,这脚本就算是raw了,我需要讲清楚如何写出一段音阶(最简单的一段中音区 C D E F G A B midi片段);如何写出一段chord(和弦),并在此和弦基础上继续写主旋律;如何变换调式调性(F# -> Ab);如何修改速度等等。

  • 编写最基础的简化指令包,虽然目前写好的raw版本是功能最全的,但是其编写midi的方式(我目前主要靠写lua脚本再 loadLua() 或者使用basicFunction1.0(啊现在应该叫FuncLib2.0)内注释掉的 load()() 来写midi)过于硬核,所以需要一个诸如 midi:0333-1 0333-2 或是 midi:E5 E5 E5 C5 这样的简单易用指令。

  • 一个音源键位包,或者一个键位函数包,用于简化脚本写midi时的打谱环节。

四、脚本输出实例试听

C大调音阶:
[upl-file uuid=e45d0254-ca7a-498c-b00d-069b2de5a74b size=257B]c-major-scale.zip[/upl-file]
Am和弦:
[upl-file uuid=d2589143-51ab-4ff7-aa9f-96734e60d4b3 size=321B]chord-example.zip[/upl-file]
时值变化:
[upl-file uuid=d9db9d92-396a-48c2-918c-2ecf3ef37e26 size=260B]duration-example.zip[/upl-file]

五、最后

脚本将会在简化指令版写完发布,愿大家都能名正言顺的咕咕咕,也愿大家都热爱生活,热爱音乐!

print fixed
简单实现改写print,将print内容写入文件生成日志。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-------------------------------------------------------------------------------
-- Print 改写. @简律纯 @gexi
-------------------------------------------------------------------------------
writeToFile = function ( str )
local filename = "print.log"
if not fileLogOut then
fileLogOut = io.open(filename, "w")
else
fileLogOut = io.open(filename, "a")
end
fileLogOut:write(os.date("%H:%M:%S",os.time()).." "..str.."\n")
fileLogOut:close()
end

function babe_tostring(...)
local num = select("#", ...);
local args = { ... };
local outs = {};
for i = 1, num do
if i > 1 then
outs[#outs + 1] = "\t";
end
outs[#outs + 1] = tostring(args[i]);
end
return table.concat(outs);
end

local just_print = print;
local babe_output = function(...)
just_print(...);

local str = babe_tostring(...);
if writeToFile then writeToFile(str) end
end
print = babe_output

yaml2table
简单实现读取yaml转为table,

暂时不支持读取注释.

后记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
-------------------------------------------------------------------------------
-- tinyyaml - YAML subset parser
-------------------------------------------------------------------------------

local table = table
local string = string
local schar = string.char
local ssub, gsub = string.sub, string.gsub
local sfind, smatch = string.find, string.match
local tinsert, tremove = table.insert, table.remove
local setmetatable = setmetatable
local pairs = pairs
local type = type
local tonumber = tonumber
local math = math
local getmetatable = getmetatable
local error = error

local UNESCAPES = {
['0'] = "\x00", z = "\x00", N = "\x85",
a = "\x07", b = "\x08", t = "\x09",
n = "\x0a", v = "\x0b", f = "\x0c",
r = "\x0d", e = "\x1b", ['\\'] = '\\',
};

-------------------------------------------------------------------------------
-- utils
local function select(list, pred)
local selected = {}
for i = 0, #list do
local v = list[i]
if v and pred(v, i) then
tinsert(selected, v)
end
end
return selected
end

local function startswith(haystack, needle)
return ssub(haystack, 1, #needle) == needle
end

local function ltrim(str)
return smatch(str, "^%s*(.-)$")
end

local function rtrim(str)
return smatch(str, "^(.-)%s*$")
end

-------------------------------------------------------------------------------
-- Implementation.
--
local class = {__meta={}}
function class.__meta.__call(cls, ...)
local self = setmetatable({}, cls)
if cls.__init then
cls.__init(self, ...)
end
return self
end

function class.def(base, typ, cls)
base = base or class
local mt = {__metatable=base, __index=base}
for k, v in pairs(base.__meta) do mt[k] = v end
cls = setmetatable(cls or {}, mt)
cls.__index = cls
cls.__metatable = cls
cls.__type = typ
cls.__meta = mt
return cls
end


local types = {
null = class:def('null'),
map = class:def('map'),
omap = class:def('omap'),
pairs = class:def('pairs'),
set = class:def('set'),
seq = class:def('seq'),
timestamp = class:def('timestamp'),
}

local Null = types.null
function Null.__tostring() return 'yaml.null' end
function Null.isnull(v)
if v == nil then return true end
if type(v) == 'table' and getmetatable(v) == Null then return true end
return false
end
local null = Null()

function types.timestamp:__init(y, m, d, h, i, s, f, z)
self.year = tonumber(y)
self.month = tonumber(m)
self.day = tonumber(d)
self.hour = tonumber(h or 0)
self.minute = tonumber(i or 0)
self.second = tonumber(s or 0)
if type(f) == 'string' and sfind(f, '^%d+$') then
self.fraction = tonumber(f) * math.pow(10, 3 - #f)
elseif f then
self.fraction = f
else
self.fraction = 0
end
self.timezone = z
end

function types.timestamp:__tostring()
return string.format(
'%04d-%02d-%02dT%02d:%02d:%02d.%03d%s',
self.year, self.month, self.day,
self.hour, self.minute, self.second, self.fraction,
self:gettz())
end

function types.timestamp:gettz()
if not self.timezone then
return ''
end
if self.timezone == 0 then
return 'Z'
end
local sign = self.timezone > 0
local z = sign and self.timezone or -self.timezone
local zh = math.floor(z)
local zi = (z - zh) * 60
return string.format(
'%s%02d:%02d', sign and '+' or '-', zh, zi)
end


local function countindent(line)
local _, j = sfind(line, '^%s+')
if not j then
return 0, line
end
return j, ssub(line, j+1)
end

local function parsestring(line, stopper)
stopper = stopper or ''
local q = ssub(line, 1, 1)
if q == ' ' or q == '\t' then
return parsestring(ssub(line, 2))
end
if q == "'" then
local i = sfind(line, "'", 2, true)
if not i then
return nil, line
end
return ssub(line, 2, i-1), ssub(line, i+1)
end
if q == '"' then
local i, buf = 2, ''
while i < #line do
local c = ssub(line, i, i)
if c == '\\' then
local n = ssub(line, i+1, i+1)
if UNESCAPES[n] ~= nil then
buf = buf..UNESCAPES[n]
elseif n == 'x' then
local h = ssub(i+2,i+3)
if sfind(h, '^[0-9a-fA-F]$') then
buf = buf..schar(tonumber(h, 16))
i = i + 2
else
buf = buf..'x'
end
else
buf = buf..n
end
i = i + 1
elseif c == q then
break
else
buf = buf..c
end
i = i + 1
end
return buf, ssub(line, i+1)
end
if q == '{' or q == '[' then -- flow style
return nil, line
end
if q == '|' or q == '>' then -- block
return nil, line
end
if q == '-' or q == ':' then
if ssub(line, 2, 2) == ' ' or #line == 1 then
return nil, line
end
end
local buf = ''
while #line > 0 do
local c = ssub(line, 1, 1)
if sfind(stopper, c, 1, true) then
break
elseif c == ':' and (ssub(line, 2, 2) == ' ' or #line == 1) then
break
elseif c == '#' and (ssub(buf, #buf, #buf) == ' ') then
break
else
buf = buf..c
end
line = ssub(line, 2)
end
return rtrim(buf), line
end

local function isemptyline(line)
return line == '' or sfind(line, '^%s*$') or sfind(line, '^%s*#')
end

local function equalsline(line, needle)
return startswith(line, needle) and isemptyline(ssub(line, #needle+1))
end

local function checkdupekey(map, key)
if map[key] ~= nil then
-- print("found a duplicate key '"..key.."' in line: "..line)
local suffix = 1
while map[key..'_'..suffix] do
suffix = suffix + 1
end
key = key ..'_'..suffix
end
return key
end

local function parseflowstyle(line, lines)
local stack = {}
while true do
if #line == 0 then
if #lines == 0 then
break
else
line = tremove(lines, 1)
end
end
local c = ssub(line, 1, 1)
if c == '#' then
line = ''
elseif c == ' ' or c == '\t' or c == '\r' or c == '\n' then
line = ssub(line, 2)
elseif c == '{' or c == '[' then
tinsert(stack, {v={},t=c})
line = ssub(line, 2)
elseif c == ':' then
local s = tremove(stack)
tinsert(stack, {v=s.v, t=':'})
line = ssub(line, 2)
elseif c == ',' then
local value = tremove(stack)
if value.t == ':' or value.t == '{' or value.t == '[' then error() end
if stack[#stack].t == ':' then
-- map
local key = tremove(stack)
key.v = checkdupekey(stack[#stack].v, key.v)
stack[#stack].v[key.v] = value.v
elseif stack[#stack].t == '{' then
-- set
stack[#stack].v[value.v] = true
elseif stack[#stack].t == '[' then
-- seq
tinsert(stack[#stack].v, value.v)
end
line = ssub(line, 2)
elseif c == '}' then
if stack[#stack].t == '{' then
if #stack == 1 then break end
stack[#stack].t = '}'
line = ssub(line, 2)
else
line = ','..line
end
elseif c == ']' then
if stack[#stack].t == '[' then
if #stack == 1 then break end
stack[#stack].t = ']'
line = ssub(line, 2)
else
line = ','..line
end
else
local s, rest = parsestring(line, ',{}[]')
if not s then
error('invalid flowstyle line: '..line)
end
tinsert(stack, {v=s, t='s'})
line = rest
end
end
return stack[1].v, line
end

local function parseblockstylestring(line, lines, indent)
if #lines == 0 then
error("failed to find multi-line scalar content")
end
local s = {}
local firstindent = -1
local endline = -1
for i = 1, #lines do
local ln = lines[i]
local idt = countindent(ln)
if idt <= indent then
break
end
if ln == '' then
tinsert(s, '')
else
if firstindent == -1 then
firstindent = idt
elseif idt < firstindent then
break
end
tinsert(s, ssub(ln, firstindent + 1))
end
endline = i
end

local striptrailing = true
local sep = '\n'
local newlineatend = true
if line == '|' then
striptrailing = true
sep = '\n'
newlineatend = true
elseif line == '|+' then
striptrailing = false
sep = '\n'
newlineatend = true
elseif line == '|-' then
striptrailing = true
sep = '\n'
newlineatend = false
elseif line == '>' then
striptrailing = true
sep = ' '
newlineatend = true
elseif line == '>+' then
striptrailing = false
sep = ' '
newlineatend = true
elseif line == '>-' then
striptrailing = true
sep = ' '
newlineatend = false
else
error('invalid blockstyle string:'..line)
end
local eonl = 0
for i = #s, 1, -1 do
if s[i] == '' then
tremove(s, i)
eonl = eonl + 1
end
end
if striptrailing then
eonl = 0
end
if newlineatend then
eonl = eonl + 1
end
for i = endline, 1, -1 do
tremove(lines, i)
end
return table.concat(s, sep)..string.rep('\n', eonl)
end

local function parsetimestamp(line)
local _, p1, y, m, d = sfind(line, '^(%d%d%d%d)%-(%d%d)%-(%d%d)')
if not p1 then
return nil, line
end
if p1 == #line then
return types.timestamp(y, m, d), ''
end
local _, p2, h, i, s = sfind(line, '^[Tt ](%d+):(%d+):(%d+)', p1+1)
if not p2 then
return types.timestamp(y, m, d), ssub(line, p1+1)
end
if p2 == #line then
return types.timestamp(y, m, d, h, i, s), ''
end
local _, p3, f = sfind(line, '^%.(%d+)', p2+1)
if not p3 then
p3 = p2
f = 0
end
local zc = ssub(line, p3+1, p3+1)
local _, p4, zs, z = sfind(line, '^ ?([%+%-])(%d+)', p3+1)
if p4 then
z = tonumber(z)
local _, p5, zi = sfind(line, '^:(%d+)', p4+1)
if p5 then
z = z + tonumber(zi) / 60
end
z = zs == '-' and -tonumber(z) or tonumber(z)
elseif zc == 'Z' then
p4 = p3 + 1
z = 0
else
p4 = p3
z = false
end
return types.timestamp(y, m, d, h, i, s, f, z), ssub(line, p4+1)
end

local function parsescalar(line, lines, indent)
line = ltrim(line)
line = gsub(line, '^%s*#.*$', '') -- comment only -> ''
line = gsub(line, '^%s*', '') -- trim head spaces

if line == '' or line == '~' then
return null
end

local ts, _ = parsetimestamp(line)
if ts then
return ts
end

local s, _ = parsestring(line)
-- startswith quote ... string
-- not startswith quote ... maybe string
if s and (startswith(line, '"') or startswith(line, "'")) then
return s
end

if startswith('!', line) then -- unexpected tagchar
error('unsupported line: '..line)
end

if equalsline(line, '{}') then
return {}
end
if equalsline(line, '[]') then
return {}
end

if startswith(line, '{') or startswith(line, '[') then
return parseflowstyle(line, lines)
end

if startswith(line, '|') or startswith(line, '>') then
return parseblockstylestring(line, lines, indent)
end

-- Regular unquoted string
line = gsub(line, '%s*#.*$', '') -- trim tail comment
local v = line
if v == 'null' or v == 'Null' or v == 'NULL'then
return null
elseif v == 'true' or v == 'True' or v == 'TRUE' then
return true
elseif v == 'false' or v == 'False' or v == 'FALSE' then
return false
elseif v == '.inf' or v == '.Inf' or v == '.INF' then
return math.huge
elseif v == '+.inf' or v == '+.Inf' or v == '+.INF' then
return math.huge
elseif v == '-.inf' or v == '-.Inf' or v == '-.INF' then
return -math.huge
elseif v == '.nan' or v == '.NaN' or v == '.NAN' then
return 0 / 0
elseif sfind(v, '^[%+%-]?[0-9]+$') or sfind(v, '^[%+%-]?[0-9]+%.$')then
return tonumber(v) -- : int
elseif sfind(v, '^[%+%-]?[0-9]+%.[0-9]+$') then
return tonumber(v)
end
return s or v
end

local parsemap; -- : func

local function parseseq(line, lines, indent)
local seq = setmetatable({}, types.seq)
if line ~= '' then
error()
end
while #lines > 0 do
-- Check for a new document
line = lines[1]
if startswith(line, '---') then
while #lines > 0 and not startswith(lines, '---') do
tremove(lines, 1)
end
return seq
end

-- Check the indent level
local level = countindent(line)
if level < indent then
return seq
elseif level > indent then
error("found bad indenting in line: ".. line)
end

local i, j = sfind(line, '%-%s+')
if not i then
i, j = sfind(line, '%-$')
if not i then
return seq
end
end
local rest = ssub(line, j+1)

if sfind(rest, '^[^\'\"%s]*:') then
-- Inline nested hash
local indent2 = j
lines[1] = string.rep(' ', indent2)..rest
tinsert(seq, parsemap('', lines, indent2))
elseif sfind(rest, '^%-%s+') then
-- Inline nested seq
local indent2 = j
lines[1] = string.rep(' ', indent2)..rest
tinsert(seq, parseseq('', lines, indent2))
elseif isemptyline(rest) then
tremove(lines, 1)
if #lines == 0 then
tinsert(seq, null)
return seq
end
if sfind(lines[1], '^%s*%-') then
local nextline = lines[1]
local indent2 = countindent(nextline)
if indent2 == indent then
-- Null seqay entry
tinsert(seq, null)
else
tinsert(seq, parseseq('', lines, indent2))
end
else
-- - # comment
-- key: value
local nextline = lines[1]
local indent2 = countindent(nextline)
tinsert(seq, parsemap('', lines, indent2))
end
elseif rest then
-- Array entry with a value
tremove(lines, 1)
tinsert(seq, parsescalar(rest, lines))
end
end
return seq
end

local function parseset(line, lines, indent)
if not isemptyline(line) then
error('not seq line: '..line)
end
local set = setmetatable({}, types.set)
while #lines > 0 do
-- Check for a new document
line = lines[1]
if startswith(line, '---') then
while #lines > 0 and not startswith(lines, '---') do
tremove(lines, 1)
end
return set
end

-- Check the indent level
local level = countindent(line)
if level < indent then
return set
elseif level > indent then
error("found bad indenting in line: ".. line)
end

local i, j = sfind(line, '%?%s+')
if not i then
i, j = sfind(line, '%?$')
if not i then
return set
end
end
local rest = ssub(line, j+1)

if sfind(rest, '^[^\'\"%s]*:') then
-- Inline nested hash
local indent2 = j
lines[1] = string.rep(' ', indent2)..rest
set[parsemap('', lines, indent2)] = true
elseif sfind(rest, '^%s+$') then
tremove(lines, 1)
if #lines == 0 then
tinsert(set, null)
return set
end
if sfind(lines[1], '^%s*%?') then
local indent2 = countindent(lines[1])
if indent2 == indent then
-- Null array entry
set[null] = true
else
set[parseseq('', lines, indent2)] = true
end
end

elseif rest then
tremove(lines, 1)
set[parsescalar(rest, lines)] = true
else
error("failed to classify line: "..line)
end
end
return set
end

function parsemap(line, lines, indent)
if not isemptyline(line) then
error('not map line: '..line)
end
local map = setmetatable({}, types.map)
while #lines > 0 do
-- Check for a new document
line = lines[1]
if startswith(line, '---') then
while #lines > 0 and not startswith(lines, '---') do
tremove(lines, 1)
end
return map
end

-- Check the indent level
local level, _ = countindent(line)
if level < indent then
return map
elseif level > indent then
error("found bad indenting in line: ".. line)
end

-- Find the key
local key
local s, rest = parsestring(line)

-- Quoted keys
if s and startswith(rest, ':') then
local sc = parsescalar(s, {}, 0)
if sc and type(sc) ~= 'string' then
key = sc
else
key = s
end
line = ssub(rest, 2)
else
error("failed to classify line: "..line)
end

key = checkdupekey(map, key)
line = ltrim(line)

if ssub(line, 1, 1) == '!' then
-- ignore type
local rh = ltrim(ssub(line, 3))
local typename = smatch(rh, '^!?[^%s]+')
line = ltrim(ssub(rh, #typename+1))
end

if not isemptyline(line) then
tremove(lines, 1)
line = ltrim(line)
map[key] = parsescalar(line, lines, indent)
else
-- An indent
tremove(lines, 1)
if #lines == 0 then
map[key] = null
return map;
end
if sfind(lines[1], '^%s*%-') then
local indent2 = countindent(lines[1])
map[key] = parseseq('', lines, indent2)
elseif sfind(lines[1], '^%s*%?') then
local indent2 = countindent(lines[1])
map[key] = parseset('', lines, indent2)
else
local indent2 = countindent(lines[1])
if indent >= indent2 then
-- Null hash entry
map[key] = null
else
map[key] = parsemap('', lines, indent2)
end
end
end
end
return map
end


-- : (list<str>)->dict
local function parsedocuments(lines)
lines = select(lines, function(s) return not isemptyline(s) end)

if sfind(lines[1], '^%%YAML') then tremove(lines, 1) end

local root = {}
local in_document = false
while #lines > 0 do
local line = lines[1]
-- Do we have a document header?
local docright;
if sfind(line, '^%-%-%-') then
-- Handle scalar documents
docright = ssub(line, 4)
tremove(lines, 1)
in_document = true
end
if docright then
if (not sfind(docright, '^%s+$') and
not sfind(docright, '^%s+#')) then
tinsert(root, parsescalar(docright, lines))
end
elseif #lines == 0 or startswith(line, '---') then
-- A naked document
tinsert(root, null)
while #lines > 0 and not sfind(lines[1], '---') do
tremove(lines, 1)
end
in_document = false
-- XXX The final '-+$' is to look for -- which ends up being an
-- error later.
elseif not in_document and #root > 0 then
-- only the first document can be explicit
error('parse error: '..line)
elseif sfind(line, '^%s*%-') then
-- An array at the root
tinsert(root, parseseq('', lines, 0))
elseif sfind(line, '^%s*[^%s]') then
-- A hash at the root
local level = countindent(line)
tinsert(root, parsemap('', lines, level))
else
-- Shouldn't get here. @lines have whitespace-only lines
-- stripped, and previous match is a line with any
-- non-whitespace. So this clause should only be reachable via
-- a perlbug where \s is not symmetric with \S

-- uncoverable statement
error('parse error: '..line)
end
end
if #root > 1 and Null.isnull(root[1]) then
tremove(root, 1)
return root
end
return root
end

--- Parse yaml string into table.
local function parse(source)
local lines = {}
for line in string.gmatch(source .. '\n', '(.-)\r?\n') do
tinsert(lines, line)
end

local docs = parsedocuments(lines)
if #docs == 1 then
return docs[1]
end

return docs
end

return {
version = 0.1,
parse = parse,
}

getFileList
简单的调用控制台获取文件夹列表实例

获取文件夹列表

使用《署名—非商业性使用—相同方式共享 4.0 协议国际版》(CC BY-NC-SA 4.0)进行授权。
https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.zh-Hans

[ toc ]

System statement
Windows supported
Linux unsupported
IOS unsupported

下载

getFileList.zip

基本信息

  • 作者: 简律纯
  • 联系方式:qq:a2c29k9
  • 版本:v1.0
  • 更新日期:2022/08/03
  • 关键词:
    --getDiceList=
    --getRootList=
  • 许可协议:CC BY-NC-SA 4.0

原理

利用os库中的os. execute执行cmd指令,达到获取文件夹列表的目的。

须知

该脚本只适配win平台,linux版本将会在后续更新(或者你也可以修改脚本,将dir改为ls即可,注意linux斜杠和反斜杠与win的区别),MA暂无解决方法。

用法

--getDiceList=nil--getRootList=nil分别用于获取dice根目录列表以及框架根目录列表,后接二级目录或更多(你需要用两个反斜杠\将他们连接)子目录来获取其列表。每次执行命令,脚本将会在dice根目录生成对应的DiceList.txt与RootList.txt,若文件太多发不出来也可以去那边查看。

举例

获取根目录列表

获取根目录二级或三级文件夹列表
获取Dice文件夹二级或更多级文件夹列表同理。

获取Dice文件夹列表

后记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
---------------------------------------------------------------------
-- @getFileList 获取文件列表.
-- Author @简律纯(qq:a2c29k9)
-- @License MIT.
-- 2022/08/03
---------------------------------------------------------------------

msg_order={["--getDiceList="]="getDiceList",["--getRootList="]="getRootList"}

read_file = function(path,mode1,mode2)
local text = ""
local file = io.open(path,mode1)
if (file ~= nil) then
text = file.read(file, mode2)
io.close(file)
else
return "没有找到文件哦~"
end
return text
end

getLineCount = function(path)
local BUFSIZE = 2^13
local f = io.input(path)
local lc = 0

while true do
local lines,rest = f:read(BUFSIZE,'*line')

if not lines then break end
if rest then lines = lines .. rest .. '\n' end
local _,t = string.gsub(lines,"%S+","")
_,t = string.gsub(lines,"\n","\n")
lc = lc + t
end

return lc
end

getDiceList = function(msg)
local path = string.sub(msg.fromMsg,#'--getDiceList='+1)
local Dir = path

if path == 'nil' then Dir = nil end

if Dir then
cmd = 'dir '.. getDiceDir()..'\\'..path..' /b >'..getDiceDir()..'//DiceList.txt'
os.execute(cmd)
else
cmd = 'dir '.. getDiceDir()..' /b >'..getDiceDir()..'//DiceList.txt'
os.execute(cmd)
end

return read_file(getDiceDir()..'//DiceList.txt','r','*a')..os.date('\n%x')..'\n文件夹与文件共 '..getLineCount(getDiceDir()..'//DiceList.txt')..' 个'

end

getRootList = function(msg)
local path = string.sub(msg.fromMsg,#'--getRootList='+1)
local Dir = path

if path == 'nil' then Dir = nil end

if Dir then
cmd = 'dir '..path..' /b >'..getDiceDir()..'//RootList.txt'
os.execute(cmd)
else
cmd = 'dir /b >'..getDiceDir()..'//RootList.txt'
os.execute(cmd)
end

return read_file(getDiceDir()..'//RootList.txt','r','*a')..os.date('\n%x')..'\n文件夹与文件共 '..getLineCount(getDiceDir()..'//RootList.txt')..' 个'

end
-->