mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-20 14:30:10 +08:00
✨ 添加mid2txt接口 (#264)
Co-authored-by: haibaraguo <haibaraguo@yeahka.com>
This commit is contained in:
parent
2bf387a57c
commit
668418c48a
@ -650,9 +650,11 @@ print("run[CQ:image,file="+j["img"]+"]")
|
|||||||
|
|
||||||
- [x] 团队听音练习
|
- [x] 团队听音练习
|
||||||
|
|
||||||
|
- [x] *.mid (解析上传的mid文件)
|
||||||
|
|
||||||
- [x] 注: 该插件需要安装timidity,安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh
|
- [x] 注: 该插件需要安装timidity,安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh
|
||||||
|
|
||||||
- [x] 符号说明: C5是中央C,后面不写数字,默认接5,Cb6<1,b代表降调,#代表升调,6比5高八度,<1代表音长×2,<2代表音长×4,<-1代表音长×0.5,<-2代表音长×0.25
|
- [x] 符号说明: C5是中央C,后面不写数字,默认接5,Cb6<1,b代表降调,#代表升调,6比5高八度,<1代表音长×2,<3代表音长×8,<-1代表音长×0.5,<-3代表音长×0.125,R是休止符
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
<details>
|
<details>
|
||||||
|
|||||||
@ -2,10 +2,13 @@
|
|||||||
package midicreate
|
package midicreate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -15,6 +18,7 @@ import (
|
|||||||
"github.com/FloatTech/zbputils/control"
|
"github.com/FloatTech/zbputils/control"
|
||||||
"github.com/FloatTech/zbputils/ctxext"
|
"github.com/FloatTech/zbputils/ctxext"
|
||||||
"github.com/FloatTech/zbputils/file"
|
"github.com/FloatTech/zbputils/file"
|
||||||
|
"github.com/FloatTech/zbputils/web"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
zero "github.com/wdvxdr1123/ZeroBot"
|
zero "github.com/wdvxdr1123/ZeroBot"
|
||||||
"github.com/wdvxdr1123/ZeroBot/message"
|
"github.com/wdvxdr1123/ZeroBot/message"
|
||||||
@ -29,7 +33,8 @@ func init() {
|
|||||||
Help: "midi音乐制作,该插件需要安装timidity,安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh\n" +
|
Help: "midi音乐制作,该插件需要安装timidity,安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh\n" +
|
||||||
"- midi制作 CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR\n" +
|
"- midi制作 CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR\n" +
|
||||||
"- 个人听音练习\n" +
|
"- 个人听音练习\n" +
|
||||||
"- 团队听音练习",
|
"- 团队听音练习\n" +
|
||||||
|
"- *.mid (解析上传的mid文件)",
|
||||||
PrivateDataFolder: "midicreate",
|
PrivateDataFolder: "midicreate",
|
||||||
})
|
})
|
||||||
cachePath := engine.DataFolder() + "cache/"
|
cachePath := engine.DataFolder() + "cache/"
|
||||||
@ -38,10 +43,10 @@ func init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
engine.OnRegex(`^midi制作\s?(.{1,1000})$`).SetBlock(true).Limit(ctxext.LimitByUser).
|
engine.OnPrefix("midi制作").SetBlock(true).Limit(ctxext.LimitByUser).
|
||||||
Handle(func(ctx *zero.Ctx) {
|
Handle(func(ctx *zero.Ctx) {
|
||||||
uid := ctx.Event.UserID
|
uid := ctx.Event.UserID
|
||||||
input := ctx.State["regex_matched"].([]string)[1]
|
input := ctx.State["args"].(string)
|
||||||
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
||||||
cmidiFile, err := str2music(input, midiFile)
|
cmidiFile, err := str2music(input, midiFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -216,6 +221,19 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
engine.On("notice/group_upload", func(ctx *zero.Ctx) bool {
|
||||||
|
return path.Ext(ctx.Event.File.Name) == ".mid"
|
||||||
|
}).SetBlock(false).Limit(ctxext.LimitByGroup).
|
||||||
|
Handle(func(ctx *zero.Ctx) {
|
||||||
|
fileURL := ctx.GetThisGroupFileUrl(ctx.Event.File.BusID, ctx.Event.File.ID)
|
||||||
|
data, err := web.GetData(fileURL)
|
||||||
|
if err != nil {
|
||||||
|
ctx.SendChain(message.Text("ERROR:", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
midStr := mid2txt(data)
|
||||||
|
ctx.SendChain(message.Text("文件名:", ctx.Event.File.Name, "\n转化的midi字符:", midStr))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -251,12 +269,12 @@ func mkMidi(filePath, input string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
clock = smf.MetricTicks(96)
|
clock smf.MetricTicks
|
||||||
tr smf.Track
|
tr smf.Track
|
||||||
)
|
)
|
||||||
|
|
||||||
tr.Add(0, smf.MetaMeter(4, 4))
|
tr.Add(0, smf.MetaMeter(4, 4))
|
||||||
tr.Add(0, smf.MetaTempo(60))
|
tr.Add(0, smf.MetaTempo(72))
|
||||||
tr.Add(0, smf.MetaInstrument("Violin"))
|
tr.Add(0, smf.MetaInstrument("Violin"))
|
||||||
tr.Add(0, midi.ProgramChange(0, gm.Instr_Violin.Value()))
|
tr.Add(0, midi.ProgramChange(0, gm.Instr_Violin.Value()))
|
||||||
|
|
||||||
@ -391,3 +409,57 @@ func processOne(note string) uint8 {
|
|||||||
}
|
}
|
||||||
return o(base, level)
|
return o(base, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mid2txt(midBytes []byte) (midStr string) {
|
||||||
|
var (
|
||||||
|
absTicksStart float64
|
||||||
|
absTicksEnd float64
|
||||||
|
startNote byte
|
||||||
|
endNote byte
|
||||||
|
defaultMetric = 960.0
|
||||||
|
defaultTrackNo = 0
|
||||||
|
)
|
||||||
|
_ = smf.ReadTracksFrom(bytes.NewReader(midBytes)).
|
||||||
|
Do(
|
||||||
|
func(te smf.TrackEvent) {
|
||||||
|
if !te.Message.IsMeta() && te.TrackNo == defaultTrackNo {
|
||||||
|
b := te.Message.Bytes()
|
||||||
|
if len(b) == 3 {
|
||||||
|
if b[0] == 0x90 && b[2] > 0 {
|
||||||
|
absTicksStart = float64(te.AbsTicks)
|
||||||
|
startNote = b[1]
|
||||||
|
}
|
||||||
|
if b[0] == 0x80 || (b[0] == 0x90 && b[2] == 0x00) {
|
||||||
|
absTicksEnd = float64(te.AbsTicks)
|
||||||
|
endNote = b[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (b[0] == 0x80 || (b[0] == 0x90 && b[2] == 0x00)) && startNote == endNote {
|
||||||
|
sign := name(b[1])
|
||||||
|
level := b[1] / 12
|
||||||
|
length := (absTicksEnd - absTicksStart) / defaultMetric
|
||||||
|
midStr += sign
|
||||||
|
if level != 5 {
|
||||||
|
midStr += strconv.Itoa(int(level))
|
||||||
|
}
|
||||||
|
pow := int(math.Round(math.Log2(length)))
|
||||||
|
if pow >= -4 && pow != 0 {
|
||||||
|
midStr += "<" + strconv.Itoa(pow)
|
||||||
|
}
|
||||||
|
startNote = 0
|
||||||
|
endNote = 0
|
||||||
|
}
|
||||||
|
if (b[0] == 0x90 && b[2] > 0) && absTicksStart > absTicksEnd {
|
||||||
|
length := (absTicksStart - absTicksEnd) / defaultMetric
|
||||||
|
pow := int(math.Round(math.Log2(length)))
|
||||||
|
if pow == 0 {
|
||||||
|
midStr += "R"
|
||||||
|
} else if pow >= -4 {
|
||||||
|
midStr += "R<" + strconv.Itoa(pow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user