@dgck81lnn/koishi-plugin-music 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +14 -0
- package/lib/index.js +125 -0
- package/lib/locales/zh.json +1 -0
- package/package.json +25 -0
- package/readme.md +5 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Context, Schema } from "koishi";
|
|
2
|
+
export declare const name = "music";
|
|
3
|
+
export declare const inject: string[];
|
|
4
|
+
export interface Config {
|
|
5
|
+
evalCommand: "glot" | "eval";
|
|
6
|
+
}
|
|
7
|
+
export declare const Config: Schema<Config>;
|
|
8
|
+
export interface Note {
|
|
9
|
+
frequency: number;
|
|
10
|
+
gain: number;
|
|
11
|
+
start: number;
|
|
12
|
+
end: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Config = exports.inject = exports.name = void 0;
|
|
4
|
+
exports.apply = apply;
|
|
5
|
+
const koishi_1 = require("koishi");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const url_1 = require("url");
|
|
8
|
+
exports.name = "music";
|
|
9
|
+
exports.inject = ["puppeteer"];
|
|
10
|
+
exports.Config = koishi_1.Schema.object({
|
|
11
|
+
evalCommand: koishi_1.Schema.union(["glot", "eval"])
|
|
12
|
+
.default("glot")
|
|
13
|
+
.description("用于安全执行 js 代码的指令。"),
|
|
14
|
+
});
|
|
15
|
+
const gutterFunc = (f) => {
|
|
16
|
+
function createCompatibilityBpmFunction(v) {
|
|
17
|
+
return new Proxy((v) => {
|
|
18
|
+
v = +v;
|
|
19
|
+
if (v > 0 && v !== Infinity)
|
|
20
|
+
bpm = createCompatibilityBpmFunction(v);
|
|
21
|
+
}, {
|
|
22
|
+
get: (_, p) => (typeof v[p] === "function" ? v[p].bind(v) : v[p]),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
let bpm = createCompatibilityBpmFunction(120);
|
|
26
|
+
let baseFrequency = 440;
|
|
27
|
+
let gain = 0.5;
|
|
28
|
+
let time = 0;
|
|
29
|
+
const notes = [];
|
|
30
|
+
f({
|
|
31
|
+
note(tone, beats, temperament = 12) {
|
|
32
|
+
this.noteHz(baseFrequency * 2 ** (tone / temperament), beats);
|
|
33
|
+
},
|
|
34
|
+
noteJust(ratio, beats) {
|
|
35
|
+
this.noteHz(baseFrequency * ratio, beats);
|
|
36
|
+
},
|
|
37
|
+
noteHz(frequency, beats) {
|
|
38
|
+
notes.push({ start: time, end: (time += (beats / +bpm) * 60), frequency, gain });
|
|
39
|
+
},
|
|
40
|
+
rest(beats) {
|
|
41
|
+
time += (beats / +bpm) * 60;
|
|
42
|
+
},
|
|
43
|
+
get bpm() {
|
|
44
|
+
return bpm;
|
|
45
|
+
},
|
|
46
|
+
set bpm(v) {
|
|
47
|
+
v = +v;
|
|
48
|
+
if (v > 0 && v !== Infinity)
|
|
49
|
+
bpm = v;
|
|
50
|
+
},
|
|
51
|
+
get baseFrequency() {
|
|
52
|
+
return baseFrequency;
|
|
53
|
+
},
|
|
54
|
+
set baseFrequency(v) {
|
|
55
|
+
v = +v;
|
|
56
|
+
if (v > 0 && v !== Infinity)
|
|
57
|
+
baseFrequency = v;
|
|
58
|
+
},
|
|
59
|
+
get gain() {
|
|
60
|
+
return gain;
|
|
61
|
+
},
|
|
62
|
+
set gain(v) {
|
|
63
|
+
v = +v;
|
|
64
|
+
if (v > 0 && v !== Infinity)
|
|
65
|
+
gain = v;
|
|
66
|
+
},
|
|
67
|
+
get time() {
|
|
68
|
+
return time;
|
|
69
|
+
},
|
|
70
|
+
set time(v) {
|
|
71
|
+
v = +v;
|
|
72
|
+
if (Number.isFinite(v))
|
|
73
|
+
time = Math.max(0, v);
|
|
74
|
+
},
|
|
75
|
+
getTime() {
|
|
76
|
+
return time;
|
|
77
|
+
},
|
|
78
|
+
setTime(v) {
|
|
79
|
+
this.time = v;
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
if (!notes.length)
|
|
83
|
+
return "";
|
|
84
|
+
return JSON.stringify(notes);
|
|
85
|
+
};
|
|
86
|
+
function apply(ctx, config) {
|
|
87
|
+
ctx.i18n.define("zh", require("./locales/zh"));
|
|
88
|
+
ctx
|
|
89
|
+
.command("musicjs <code:rawtext>", { strictOptions: true })
|
|
90
|
+
.action(async ({ session }, code) => {
|
|
91
|
+
if (!code)
|
|
92
|
+
return session.text(".require-code");
|
|
93
|
+
try {
|
|
94
|
+
new Function(code);
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
return koishi_1.h.text(String(e));
|
|
98
|
+
}
|
|
99
|
+
let gutteredCode = `(${gutterFunc})(${new Function("$", "with($){" + code + "}")})`;
|
|
100
|
+
if (config.evalCommand !== "eval")
|
|
101
|
+
gutteredCode = `process.stdout.write(${gutteredCode})`;
|
|
102
|
+
ctx.logger.debug(config.evalCommand, gutteredCode);
|
|
103
|
+
const evalArgv = koishi_1.Argv.parse(config.evalCommand);
|
|
104
|
+
evalArgv.tokens.push({
|
|
105
|
+
content: koishi_1.h.escape(gutteredCode),
|
|
106
|
+
inters: [],
|
|
107
|
+
quoted: true,
|
|
108
|
+
terminator: "",
|
|
109
|
+
});
|
|
110
|
+
const data = (0, koishi_1.h)("", await session.execute(evalArgv, true)).toString(true);
|
|
111
|
+
if (!data)
|
|
112
|
+
session.text(".no-note");
|
|
113
|
+
try {
|
|
114
|
+
ctx.logger.debug(JSON.parse(data));
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return koishi_1.h.text(data);
|
|
118
|
+
}
|
|
119
|
+
const page = await ctx.puppeteer.page();
|
|
120
|
+
await page.goto((0, url_1.pathToFileURL)((0, path_1.resolve)(__dirname, "../browser/index.html")).href);
|
|
121
|
+
const base64 = (await page.evaluate(`synth(${data}).then(encodeWav).then(arrayBufferToBase64)`));
|
|
122
|
+
page.close().catch(() => { });
|
|
123
|
+
return koishi_1.h.audio("data:audio/wav;base64," + base64);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"commands":{"musicjs":{"description":"用 JavaScript 代码演奏旋律","usage":"函数及其参数说明:\n note(tone: number, beats: number, temperament = 12) 创建平均律音符\n tone 音符相对于基准音的偏移(默认情况下,单位为半音)\n beats 音符时长(拍)\n temperament 平均律的音阶数;决定了多少 tone 为一个八度\n noteJust(ratio: number, beats: number) 创建纯律音符\n ratio 音符音高与基准音频率的比值\n beats 音符时长(拍)\n noteHz(frequency: number, beats: number) 创建指定频率音高的音符\n frequency 频率(赫兹)\n beats 音符时长(拍)\n rest(beats: number) 休止\n beats 休止时长(拍)\n变量说明——修改这些变量会影响后续创建音符的属性:\n bpm: Number = 120 每分钟拍数\n 由于兼容原因,除非给该变量赋过值,否则读取该变量得到的是一个可以被隐式转换成数字的函数。\n baseFrequency = 440 基准音频率\n gain = 0.5 音量\n time = 0 当前时间(秒)\n 调用 note、rest 等函数后此变量的值会自动增加。\n 修改此值后再添加音符可以使不同音符同时播放。","messages":{"require-code":"缺少代码。","no-note":"未定义任何音符。"}}}}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dgck81lnn/koishi-plugin-music",
|
|
3
|
+
"description": "Synthesize melodies in Koishi",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"typings": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib",
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"koishi": "4.17.12"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"koishi-plugin-puppeteer": "^3.9.0"
|
|
17
|
+
},
|
|
18
|
+
"koishi": {
|
|
19
|
+
"service": {
|
|
20
|
+
"required": [
|
|
21
|
+
"puppeteer"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|