diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 778f021..4fd50e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,6 +68,7 @@ jobs: del ./backend-python/rwkv_pip/cpp/librwkv.dylib del ./backend-python/rwkv_pip/cpp/librwkv.so (Get-Content -Path ./backend-golang/app.go) -replace "//go:custom_build windows ", "" | Set-Content -Path ./backend-golang/app.go + (Get-Content -Path ./backend-golang/utils.go) -replace "//go:custom_build windows ", "" | Set-Content -Path ./backend-golang/utils.go make Rename-Item -Path "build/bin/RWKV-Runner.exe" -NewName "RWKV-Runner_windows_x64.exe" diff --git a/.gitignore b/.gitignore index 246fbdf..267db7f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ __pycache__ *.st *.safetensors *.bin +*.mid /config.json /cache.json /presets.json diff --git a/backend-golang/file.go b/backend-golang/file.go index e4264e8..775b083 100644 --- a/backend-golang/file.go +++ b/backend-golang/file.go @@ -14,6 +14,13 @@ import ( wruntime "github.com/wailsapp/wails/v2/pkg/runtime" ) +func (a *App) SaveFile(path string, savedContent []byte) error { + if err := os.WriteFile(a.exDir+path, savedContent, 0644); err != nil { + return err + } + return nil +} + func (a *App) SaveJson(fileName string, jsonData any) error { text, err := json.MarshalIndent(jsonData, "", " ") if err != nil { @@ -195,3 +202,8 @@ func (a *App) OpenFileFolder(path string, relative bool) error { } return errors.New("unsupported OS") } + +func (a *App) StartFile(path string) error { + _, err := CmdHelper(path) + return err +} diff --git a/backend-golang/utils.go b/backend-golang/utils.go index 64594fe..b5e7f99 100644 --- a/backend-golang/utils.go +++ b/backend-golang/utils.go @@ -15,33 +15,51 @@ import ( "runtime" "strconv" "strings" + "syscall" ) +func CmdHelper(args ...string) (*exec.Cmd, error) { + if runtime.GOOS != "windows" { + return nil, errors.New("unsupported OS") + } + filename := "./cmd-helper.bat" + _, err := os.Stat(filename) + if err != nil { + if err := os.WriteFile(filename, []byte("start %*"), 0644); err != nil { + return nil, err + } + } + cmdHelper, err := filepath.Abs(filename) + if err != nil { + return nil, err + } + + if strings.Contains(cmdHelper, " ") { + for _, arg := range args { + if strings.Contains(arg, " ") { + return nil, errors.New("path contains space") // golang bug https://github.com/golang/go/issues/17149#issuecomment-473976818 + } + } + } + cmd := exec.Command(cmdHelper, args...) + cmd.SysProcAttr = &syscall.SysProcAttr{} + //go:custom_build windows cmd.SysProcAttr.HideWindow = true + err = cmd.Start() + if err != nil { + return nil, err + } + return cmd, nil +} + func Cmd(args ...string) (string, error) { switch platform := runtime.GOOS; platform { case "windows": - if err := os.WriteFile("./cmd-helper.bat", []byte("start %*"), 0644); err != nil { - return "", err - } - cmdHelper, err := filepath.Abs("./cmd-helper") + cmd, err := CmdHelper(args...) if err != nil { return "", err } - - if strings.Contains(cmdHelper, " ") { - for _, arg := range args { - if strings.Contains(arg, " ") { - return "", errors.New("path contains space") // golang bug https://github.com/golang/go/issues/17149#issuecomment-473976818 - } - } - } - - cmd := exec.Command(cmdHelper, args...) - out, err := cmd.CombinedOutput() - if err != nil { - return "", err - } - return string(out), nil + cmd.Wait() + return "", nil case "darwin": ex, err := os.Executable() if err != nil { diff --git a/frontend/src/_locales/ja/main.json b/frontend/src/_locales/ja/main.json index 8efdec8..6337b0b 100644 --- a/frontend/src/_locales/ja/main.json +++ b/frontend/src/_locales/ja/main.json @@ -316,5 +316,6 @@ "Current Instrument": "現在の楽器", "Please convert model to GGML format first": "モデルをGGML形式に変換してください", "Convert To GGML Format": "GGML形式に変換", - "CPU (rwkv.cpp, Faster)": "CPU (rwkv.cpp, 高速)" + "CPU (rwkv.cpp, Faster)": "CPU (rwkv.cpp, 高速)", + "Play With External Player": "外部プレーヤーで再生" } \ No newline at end of file diff --git a/frontend/src/_locales/zh-hans/main.json b/frontend/src/_locales/zh-hans/main.json index 1862911..4890e42 100644 --- a/frontend/src/_locales/zh-hans/main.json +++ b/frontend/src/_locales/zh-hans/main.json @@ -316,5 +316,6 @@ "Current Instrument": "当前乐器", "Please convert model to GGML format first": "请先将模型转换为GGML格式", "Convert To GGML Format": "转换为GGML格式", - "CPU (rwkv.cpp, Faster)": "CPU (rwkv.cpp, 更快)" + "CPU (rwkv.cpp, Faster)": "CPU (rwkv.cpp, 更快)", + "Play With External Player": "使用外部播放器播放" } \ No newline at end of file diff --git a/frontend/src/pages/Composition.tsx b/frontend/src/pages/Composition.tsx index fd54ecf..0caaf1f 100644 --- a/frontend/src/pages/Composition.tsx +++ b/frontend/src/pages/Composition.tsx @@ -21,7 +21,9 @@ import { FileExists, OpenFileFolder, OpenMidiPort, - OpenSaveFileDialogBytes + OpenSaveFileDialogBytes, + SaveFile, + StartFile } from '../../wailsjs/go/backend_golang/App'; import { getServerRoot, getSoundFont, toastWithButton } from '../utils'; import { CompositionParams } from '../types/composition'; @@ -98,6 +100,31 @@ const CompositionPanel: FC = observer(() => { } }, []); + const externalPlayListener = () => { + const params = commonStore.compositionParams; + const saveAndPlay = async (midi: ArrayBuffer, path: string) => { + await SaveFile(path, Array.from(new Uint8Array(midi))); + StartFile(path); + }; + if (params.externalPlay) { + if (params.midi) { + setTimeout(() => { + playerRef.current?.stop(); + }); + saveAndPlay(params.midi, './midi/last.mid').catch((e: string) => { + if (e.includes('being used')) + saveAndPlay(params.midi!, './midi/last-2.mid'); + }); + } + } + }; + useEffect(() => { + playerRef.current?.addEventListener('start', externalPlayListener); + return () => { + playerRef.current?.removeEventListener('start', externalPlayListener); + }; + }, [params.externalPlay]); + useEffect(() => { if (!(commonStore.activeMidiDeviceIndex in commonStore.midiPorts)) { commonStore.setActiveMidiDeviceIndex(-1); @@ -123,9 +150,12 @@ const CompositionPanel: FC = observer(() => { }); updateNs(ns); if (autoPlay) { - setTimeout(() => { - playerRef.current?.start(); - }); + if (commonStore.compositionParams.externalPlay) + externalPlayListener(); + else + setTimeout(() => { + playerRef.current?.start(); + }); } }); }); @@ -268,6 +298,16 @@ const CompositionPanel: FC = observer(() => { setSoundFont(); }} /> } + { + commonStore.platform === 'windows' && + { + setParams({ + externalPlay: data.checked as boolean + }); + }} /> + } { setParams({ diff --git a/frontend/src/stores/commonStore.ts b/frontend/src/stores/commonStore.ts index d1e8a8e..34962eb 100644 --- a/frontend/src/stores/commonStore.ts +++ b/frontend/src/stores/commonStore.ts @@ -94,6 +94,7 @@ class CommonStore { topP: 0.8, autoPlay: true, useLocalSoundFont: false, + externalPlay: false, midi: null, ns: null }; diff --git a/frontend/src/types/composition.ts b/frontend/src/types/composition.ts index 6bf5f08..64dfc2b 100644 --- a/frontend/src/types/composition.ts +++ b/frontend/src/types/composition.ts @@ -9,6 +9,7 @@ export type CompositionParams = { topP: number, autoPlay: boolean, useLocalSoundFont: boolean, + externalPlay: boolean, midi: ArrayBuffer | null, ns: NoteSequence | null } diff --git a/frontend/wailsjs/go/backend_golang/App.d.ts b/frontend/wailsjs/go/backend_golang/App.d.ts index 10e6503..b594cc1 100755 --- a/frontend/wailsjs/go/backend_golang/App.d.ts +++ b/frontend/wailsjs/go/backend_golang/App.d.ts @@ -58,8 +58,12 @@ export function ReadJson(arg1:string):Promise; export function RestartApp():Promise; +export function SaveFile(arg1:string,arg2:Array):Promise; + export function SaveJson(arg1:string,arg2:any):Promise; +export function StartFile(arg1:string):Promise; + export function StartServer(arg1:string,arg2:number,arg3:string,arg4:boolean,arg5:boolean,arg6:boolean):Promise; export function StartWebGPUServer(arg1:number,arg2:string):Promise; diff --git a/frontend/wailsjs/go/backend_golang/App.js b/frontend/wailsjs/go/backend_golang/App.js index 8d16582..3be1c13 100755 --- a/frontend/wailsjs/go/backend_golang/App.js +++ b/frontend/wailsjs/go/backend_golang/App.js @@ -114,10 +114,18 @@ export function RestartApp() { return window['go']['backend_golang']['App']['RestartApp'](); } +export function SaveFile(arg1, arg2) { + return window['go']['backend_golang']['App']['SaveFile'](arg1, arg2); +} + export function SaveJson(arg1, arg2) { return window['go']['backend_golang']['App']['SaveJson'](arg1, arg2); } +export function StartFile(arg1) { + return window['go']['backend_golang']['App']['StartFile'](arg1); +} + export function StartServer(arg1, arg2, arg3, arg4, arg5, arg6) { return window['go']['backend_golang']['App']['StartServer'](arg1, arg2, arg3, arg4, arg5, arg6); }