@@ -15,26 +15,28 @@ import (
1515 "time"
1616
1717 ctrl "github.com/FloatTech/zbpctrl"
18+ "github.com/FloatTech/zbputils/binary"
1819 "github.com/FloatTech/zbputils/control"
1920 "github.com/FloatTech/zbputils/ctxext"
2021 "github.com/FloatTech/zbputils/file"
2122 "github.com/FloatTech/zbputils/web"
2223 "github.com/pkg/errors"
2324 zero "github.com/wdvxdr1123/ZeroBot"
2425 "github.com/wdvxdr1123/ZeroBot/message"
25- "gitlab.com/gomidi/midi/gm"
2626 "gitlab.com/gomidi/midi/v2"
2727 "gitlab.com/gomidi/midi/v2/smf"
2828)
2929
3030func init () {
3131 engine := control .Register ("midicreate" , & ctrl.Options [* zero.Ctx ]{
3232 DisableOnDefault : false ,
33- Help : "midi音乐制作,该插件需要安装timidity,安装脚本可参考https ://gitcode.net/anto_july/midi/-/raw/master/timidity.sh\n " +
33+ Help : "midi音乐制作,该插件需要安装timidity,linux安装脚本可参考https ://gitcode.net/anto_july/midi/-/raw/master/timidity.sh,windows安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.bat,windows需要管理员模式运行 \n " +
3434 "- midi制作 CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR\n " +
3535 "- 个人听音练习\n " +
3636 "- 团队听音练习\n " +
37- "- *.mid (解析上传的mid文件)" ,
37+ "- *.mid (midi 转 txt)\n " +
38+ "- midi制作*.txt (txt 转 midi)\n " +
39+ "- 设置音色40 (0~127)" ,
3840 PrivateDataFolder : "midicreate" ,
3941 })
4042 cachePath := engine .DataFolder () + "cache/"
@@ -48,7 +50,7 @@ func init() {
4850 uid := ctx .Event .UserID
4951 input := ctx .State ["args" ].(string )
5052 midiFile := cachePath + strconv .FormatInt (uid , 10 ) + time .Now ().Format ("20060102150405" ) + "_midicreate.mid"
51- cmidiFile , err := str2music (input , midiFile )
53+ cmidiFile , err := str2music (ctx , input , midiFile )
5254 if err != nil {
5355 if file .IsExist (midiFile ) {
5456 ctx .UploadThisGroupFile (file .BOTPATH + "/" + midiFile , filepath .Base (midiFile ), "" )
@@ -87,7 +89,7 @@ func init() {
8789 target := uint8 (55 + rand .Intn (34 ))
8890 answer := name (target ) + strconv .Itoa (int (target / 12 ))
8991 midiFile := cachePath + strconv .FormatInt (uid , 10 ) + time .Now ().Format ("20060102150405" ) + "_midicreate.mid"
90- cmidiFile , err := str2music (answer , midiFile )
92+ cmidiFile , err := str2music (ctx , answer , midiFile )
9193 if err != nil {
9294 ctx .SendChain (message .Text ("ERROR:听音练习结束, 无法转换midi文件, " , err ))
9395 return
@@ -137,7 +139,7 @@ func init() {
137139 ),
138140 )
139141 midiFile = cachePath + strconv .FormatInt (uid , 10 ) + time .Now ().Format ("20060102150405" ) + "_midicreate.mid"
140- cmidiFile , err = str2music (c .Event .Message .String (), midiFile )
142+ cmidiFile , err = str2music (ctx , c .Event .Message .String (), midiFile )
141143 if err != nil {
142144 ctx .SendChain (message .Text ("ERROR: can't convert midi file," , err ))
143145 return
@@ -172,7 +174,7 @@ func init() {
172174 target = uint8 (55 + rand .Intn (34 ))
173175 answer = name (target ) + strconv .Itoa (int (target / 12 ))
174176 midiFile = cachePath + strconv .FormatInt (uid , 10 ) + time .Now ().Format ("20060102150405" ) + "_midicreate.mid"
175- cmidiFile , err = str2music (answer , midiFile )
177+ cmidiFile , err = str2music (ctx , answer , midiFile )
176178 if err != nil {
177179 ctx .SendChain (message .Text ("ERROR:听音练习结束, 无法转换midi文件, " , err ))
178180 return
@@ -193,7 +195,7 @@ func init() {
193195 )
194196 time .Sleep (time .Millisecond * 500 )
195197 midiFile = cachePath + strconv .FormatInt (uid , 10 ) + time .Now ().Format ("20060102150405" ) + "_midicreate.mid"
196- cmidiFile , err = str2music (c .Event .Message .String (), midiFile )
198+ cmidiFile , err = str2music (ctx , c .Event .Message .String (), midiFile )
197199 if err != nil {
198200 ctx .SendChain (message .Text ("ERROR: can't convert midi file," , err ))
199201 return
@@ -231,8 +233,54 @@ func init() {
231233 ctx .SendChain (message .Text ("ERROR:" , err ))
232234 return
233235 }
234- midStr := mid2txt (data )
235- ctx .SendChain (message .Text ("文件名:" , ctx .Event .File .Name , "\n 转化的midi字符:" , midStr ))
236+ s , err := smf .ReadFrom (bytes .NewReader (data ))
237+ if err != nil {
238+ ctx .SendChain (message .Text ("ERROR:" , err ))
239+ return
240+ }
241+ for i := 0 ; i < int (s .NumTracks ()); i ++ {
242+ midStr := mid2txt (data , i )
243+ if err != nil {
244+ ctx .SendChain (message .Text ("ERROR:" , err ))
245+ return
246+ }
247+ fileName := strings .ReplaceAll (cachePath + "/" + ctx .Event .File .Name , ".mid" , fmt .Sprintf ("-%d.txt" , i ))
248+ _ = os .WriteFile (fileName , binary .StringToBytes (midStr ), 0666 )
249+ ctx .UploadThisGroupFile (file .BOTPATH + "/" + fileName , filepath .Base (fileName ), "" )
250+ }
251+ })
252+ engine .On ("notice/group_upload" , func (ctx * zero.Ctx ) bool {
253+ return path .Ext (ctx .Event .File .Name ) == ".txt" && strings .Contains (ctx .Event .File .Name , "midi制作" )
254+ }).SetBlock (false ).Limit (ctxext .LimitByGroup ).
255+ Handle (func (ctx * zero.Ctx ) {
256+ fileURL := ctx .GetThisGroupFileUrl (ctx .Event .File .BusID , ctx .Event .File .ID )
257+ data , err := web .GetData (fileURL )
258+ if err != nil {
259+ ctx .SendChain (message .Text ("ERROR:" , err ))
260+ return
261+ }
262+ uid := ctx .Event .UserID
263+ midiFile := cachePath + strconv .FormatInt (uid , 10 ) + time .Now ().Format ("20060102150405" ) + "_midicreate.mid"
264+ cmidiFile , err := str2music (ctx , binary .BytesToString (data ), midiFile )
265+ if err != nil {
266+ ctx .SendChain (message .Text ("ERROR:无法转换midi文件," , err ))
267+ return
268+ }
269+ ctx .SendChain (message .Record ("file:///" + file .BOTPATH + "/" + cmidiFile ))
270+ })
271+ engine .OnPrefix ("设置音色" ).SetBlock (true ).
272+ Handle (func (ctx * zero.Ctx ) {
273+ param := ctx .State ["args" ].(string )
274+ timbre , err := strconv .Atoi (param )
275+ if err != nil {
276+ ctx .SendChain (message .Text ("ERROR:" , err ))
277+ }
278+ err = setTimbreMode (ctx , int64 (timbre ))
279+ if err != nil {
280+ ctx .SendChain (message .Reply (ctx .Event .MessageID ), message .Text (err ))
281+ return
282+ }
283+ ctx .SendChain (message .Reply (ctx .Event .MessageID ), message .Text ("成功" ))
236284 })
237285}
238286
@@ -253,8 +301,8 @@ var (
253301 }
254302)
255303
256- func str2music (input , midiFile string ) (cmidiFile string , err error ) {
257- err = mkMidi (midiFile , input )
304+ func str2music (ctx * zero. Ctx , input , midiFile string ) (cmidiFile string , err error ) {
305+ err = mkMidi (ctx , midiFile , input )
258306 if err != nil {
259307 return
260308 }
@@ -264,7 +312,7 @@ func str2music(input, midiFile string) (cmidiFile string, err error) {
264312 return
265313}
266314
267- func mkMidi (filePath , input string ) error {
315+ func mkMidi (ctx * zero. Ctx , filePath , input string ) error {
268316 if file .IsExist (filePath ) {
269317 return nil
270318 }
@@ -276,7 +324,8 @@ func mkMidi(filePath, input string) error {
276324 tr .Add (0 , smf .MetaMeter (4 , 4 ))
277325 tr .Add (0 , smf .MetaTempo (72 ))
278326 tr .Add (0 , smf .MetaInstrument ("Violin" ))
279- tr .Add (0 , midi .ProgramChange (0 , gm .Instr_Violin .Value ()))
327+ timbre := getTimbreMode (ctx )
328+ tr .Add (0 , midi .ProgramChange (0 , uint8 (timbre )))
280329
281330 k := strings .ReplaceAll (input , " " , "" )
282331
@@ -410,46 +459,43 @@ func processOne(note string) uint8 {
410459 return o (base , level )
411460}
412461
413- func mid2txt (midBytes []byte ) (midStr string ) {
462+ func mid2txt (midBytes []byte , trackNo int ) (midStr string ) {
414463 var (
415- absTicksStart float64
416- absTicksEnd float64
417- startNote byte
418- endNote byte
419- defaultMetric = 960.0
420- defaultTrackNo = 0
464+ absTicksStart float64
465+ absTicksEnd float64
466+ startNote byte
467+ endNote byte
468+ defaultMetric = 960.0
421469 )
422- _ = smf .ReadTracksFrom (bytes .NewReader (midBytes )).
470+ _ = smf .ReadTracksFrom (bytes .NewReader (midBytes ), trackNo ).
423471 Do (
424472 func (te smf.TrackEvent ) {
425- if ! te .Message .IsMeta () && te . TrackNo == defaultTrackNo {
473+ if ! te .Message .IsMeta () {
426474 b := te .Message .Bytes ()
427- if len (b ) == 3 {
428- if b [0 ] == 0x90 && b [2 ] > 0 {
429- absTicksStart = float64 (te .AbsTicks )
430- startNote = b [1 ]
431- }
432- if b [0 ] == 0x80 || (b [0 ] == 0x90 && b [2 ] == 0x00 ) {
433- absTicksEnd = float64 (te .AbsTicks )
434- endNote = b [1 ]
435- }
475+ if te .Message .Is (midi .NoteOnMsg ) && b [2 ] > 0 {
476+ absTicksStart = float64 (te .AbsTicks )
477+ startNote = b [1 ]
436478 }
437- if (b [0 ] == 0x80 || (b [0 ] == 0x90 && b [2 ] == 0x00 )) && startNote == endNote {
438- sign := name (b [1 ])
439- level := b [1 ] / 12
440- length := (absTicksEnd - absTicksStart ) / defaultMetric
441- midStr += sign
442- if level != 5 {
443- midStr += strconv .Itoa (int (level ))
444- }
445- pow := int (math .Round (math .Log2 (length )))
446- if pow >= - 4 && pow != 0 {
447- midStr += "<" + strconv .Itoa (pow )
479+ if te .Message .Is (midi .NoteOffMsg ) || (te .Message .Is (midi .NoteOnMsg ) && b [2 ] == 0x00 ) {
480+ absTicksEnd = float64 (te .AbsTicks )
481+ endNote = b [1 ]
482+ if startNote == endNote {
483+ sign := name (b [1 ])
484+ level := b [1 ] / 12
485+ length := (absTicksEnd - absTicksStart ) / defaultMetric
486+ midStr += sign
487+ if level != 5 {
488+ midStr += strconv .Itoa (int (level ))
489+ }
490+ pow := int (math .Round (math .Log2 (length )))
491+ if pow >= - 4 && pow != 0 {
492+ midStr += "<" + strconv .Itoa (pow )
493+ }
494+ startNote = 0
495+ endNote = 0
448496 }
449- startNote = 0
450- endNote = 0
451497 }
452- if (b [ 0 ] == 0x90 && b [2 ] > 0 ) && absTicksStart > absTicksEnd {
498+ if (te . Message . Is ( midi . NoteOnMsg ) && b [2 ] > 0 ) && absTicksStart > absTicksEnd {
453499 length := (absTicksStart - absTicksEnd ) / defaultMetric
454500 pow := int (math .Round (math .Log2 (length )))
455501 if pow == 0 {
@@ -463,3 +509,31 @@ func mid2txt(midBytes []byte) (midStr string) {
463509 )
464510 return
465511}
512+
513+ func setTimbreMode (ctx * zero.Ctx , timbre int64 ) error {
514+ gid := ctx .Event .GroupID
515+ if gid == 0 {
516+ gid = - ctx .Event .UserID
517+ }
518+ if timbre < 0 || timbre > 127 {
519+ return errors .New ("音色应该在0~127之间" )
520+ }
521+ m , ok := ctx .State ["manager" ].(* ctrl.Control [* zero.Ctx ])
522+ if ! ok {
523+ return errors .New ("no such plugin" )
524+ }
525+ return m .SetData (gid , timbre )
526+ }
527+
528+ func getTimbreMode (ctx * zero.Ctx ) (index int64 ) {
529+ gid := ctx .Event .GroupID
530+ if gid == 0 {
531+ gid = - ctx .Event .UserID
532+ }
533+ m , ok := ctx .State ["manager" ].(* ctrl.Control [* zero.Ctx ])
534+ if ok {
535+ index := m .GetData (gid )
536+ return index
537+ }
538+ return 40
539+ }
0 commit comments