diff --git a/echo/messageidmap.go b/echo/messageidmap.go index f970f778..fb9ec302 100644 --- a/echo/messageidmap.go +++ b/echo/messageidmap.go @@ -1,9 +1,16 @@ package echo import ( + "fmt" + "log" "math/rand" + "strconv" + "strings" "sync" "time" + + "github.com/hoshinonyaruko/gensokyo/config" + "github.com/hoshinonyaruko/gensokyo/idmap" ) type messageRecord struct { @@ -55,7 +62,38 @@ func GetLazyMessagesId(groupID string) string { randomIndex := rand.Intn(len(recentMessages)) randomMessageID = recentMessages[randomIndex] } else { - randomMessageID = "" + msgType := GetMessageIDByUseridOrGroupidv2(config.GetAppIDStr(), groupID) + if strings.HasPrefix(msgType, "guild") { + randomMessageID = "1000" // 频道主动信息 + } else { + randomMessageID = "" + } } return randomMessageID } + +// 通过user_id获取messageID +func GetMessageIDByUseridOrGroupidv2(appID string, userID interface{}) string { + // 从appID和userID生成key + var userIDStr string + switch u := userID.(type) { + case int: + userIDStr = strconv.Itoa(u) + case int64: + userIDStr = strconv.FormatInt(u, 10) + case float64: + userIDStr = strconv.FormatFloat(u, 'f', 0, 64) + case string: + userIDStr = u + default: + // 可能需要处理其他类型或报错 + return "" + } + //将真实id转为int + userid64, err := idmap.StoreIDv2(userIDStr) + if err != nil { + log.Fatalf("Error storing ID 241: %v", err) + } + key := appID + "_" + fmt.Sprint(userid64) + return GetMsgIDByKey(key) +} diff --git a/git b/git deleted file mode 100644 index e69de29b..00000000 diff --git a/go.mod b/go.mod index 69c24bbb..5b84032e 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,11 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mvdan/xurls v1.1.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect + github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea // indirect + modernc.org/libc v1.8.1 // indirect + modernc.org/mathutil v1.2.2 // indirect + modernc.org/memory v1.0.4 // indirect ) replace github.com/tencent-connect/botgo => ./botgo diff --git a/go.sum b/go.sum index 4f06c76d..b485bef4 100644 --- a/go.sum +++ b/go.sum @@ -74,6 +74,7 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -101,6 +102,8 @@ github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZ github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -123,6 +126,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea h1:sl1pYm1kHtIndckTY8YDt+QFt77vI0JnKHP0U8rZtKc= +github.com/wdvxdr1123/go-silk v0.0.0-20220304095002-f67345df09ea/go.mod h1:ecFKZPX81BaB70I6ruUgEwYcDOtuNgJGnjdK+MIl5ko= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= @@ -152,9 +157,11 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -199,6 +206,13 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/libc v1.8.1 h1:y9oPIhwcaFXxX7kMp6Qb2ZLKzr0mDkikWN3CV5GS63o= +modernc.org/libc v1.8.1/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc= mvdan.cc/xurls v1.1.0/go.mod h1:TNWuhvo+IqbUCmtUIb/3LJSQdrzel8loVpgFm0HikbI= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/handlers/send_group_msg.go b/handlers/send_group_msg.go index d288c61f..87d6717a 100644 --- a/handlers/send_group_msg.go +++ b/handlers/send_group_msg.go @@ -212,7 +212,29 @@ func generateGroupMessage(id string, foundItems map[string][]string, messageText SrvSendMsg: true, } } else if voiceURLs, ok := foundItems["base64_record"]; ok && len(voiceURLs) > 0 { - // 目前不支持发语音 todo 适配base64 slik + // 适配base64 slik + if base64_record, ok := foundItems["base64_record"]; ok && len(base64_record) > 0 { + // 解码base64语音数据 + fileRecordData, err := base64.StdEncoding.DecodeString(base64_record[0]) + if err != nil { + mylog.Printf("failed to decode base64 record: %v", err) + return nil + } + // 将解码的语音数据转换回base64格式并上传 + imageURL, err := images.UploadBase64RecordToServer(base64.StdEncoding.EncodeToString(fileRecordData)) + if err != nil { + mylog.Printf("failed to upload base64 record: %v", err) + return nil + } + // 创建RichMediaMessage并返回 + return &dto.RichMediaMessage{ + EventID: id, + FileType: 3, // 3代表语音 + URL: imageURL, + Content: "", // 这个字段文档没有了 + SrvSendMsg: true, + } + } } else if base64_image, ok := foundItems["base64_image"]; ok && len(base64_image) > 0 { // todo 适配base64图片 //因为QQ群没有 form方式上传,所以在gensokyo内置了图床,需公网,或以lotus方式连接位于公网的gensokyo diff --git a/handlers/send_guild_channel_msg.go b/handlers/send_guild_channel_msg.go index 665cd5df..eb587235 100644 --- a/handlers/send_guild_channel_msg.go +++ b/handlers/send_guild_channel_msg.go @@ -184,7 +184,7 @@ func GenerateReplyMessage(id string, foundItems map[string][]string, messageText MsgType: 0, // Assuming type 0 for images } } else if voiceURLs, ok := foundItems["base64_record"]; ok && len(voiceURLs) > 0 { - //还不支持发语音 + //频道 还不支持发语音 // Sending a voice message // reply = dto.MessageToCreate{ // EventID: id, diff --git a/images/upload_api.go b/images/upload_api.go index 4b229c0f..cec620aa 100644 --- a/images/upload_api.go +++ b/images/upload_api.go @@ -52,6 +52,45 @@ func UploadBase64ImageToServer(base64Image string) (string, error) { return "", errors.New("local server uses a private address; image upload failed") } +// 将base64语音通过lotus转换成url +func UploadBase64RecordToServer(base64Image string) (string, error) { + // 根据serverPort确定协议 + protocol := "http" + serverPort := config.GetPortValue() + if serverPort == "443" { + protocol = "https" + } + + if config.GetLotusValue() { + serverDir := config.GetServer_dir() + url := fmt.Sprintf("%s://%s:%s/uploadrecord", protocol, serverDir, serverPort) + + resp, err := postRecordToServer(base64Image, url) + if err != nil { + return "", err + } + return resp, nil + } + + serverDir := config.GetServer_dir() + // 当端口是443时,使用HTTP和444端口 + if serverPort == "443" { + protocol = "http" + serverPort = "444" + } + + if isPublicAddress(serverDir) { + url := fmt.Sprintf("%s://127.0.0.1:%s/uploadrecord", protocol, serverPort) + + resp, err := postRecordToServer(base64Image, url) + if err != nil { + return "", err + } + return resp, nil + } + return "", errors.New("local server uses a private address; image record failed") +} + // 请求图床api(图床就是lolus为false的gensokyo) func postImageToServer(base64Image, targetURL string) (string, error) { data := url.Values{} @@ -84,6 +123,38 @@ func postImageToServer(base64Image, targetURL string) (string, error) { return "", fmt.Errorf("URL not found in response") } +// 请求语音床api(图床就是lolus为false的gensokyo) +func postRecordToServer(base64Image, targetURL string) (string, error) { + data := url.Values{} + data.Set("base64Record", base64Image) // 修改字段名以与服务器匹配 + + resp, err := http.PostForm(targetURL, data) + if err != nil { + return "", fmt.Errorf("failed to send request: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("error response from server: %s", resp.Status) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("failed to read response body: %v", err) + } + + var responseMap map[string]interface{} + if err := json.Unmarshal(body, &responseMap); err != nil { + return "", fmt.Errorf("failed to unmarshal response: %v", err) + } + + if value, ok := responseMap["url"]; ok { + return fmt.Sprintf("%v", value), nil + } + + return "", fmt.Errorf("URL not found in response") +} + // 判断是否公网ip 填写域名也会被认为是公网,但需要用户自己确保域名正确解析到gensokyo所在的ip地址 func isPublicAddress(addr string) bool { if strings.Contains(addr, "localhost") || strings.HasPrefix(addr, "127.") || strings.HasPrefix(addr, "192.168.") { diff --git a/main.go b/main.go index 42d74ab6..dc8f8736 100644 --- a/main.go +++ b/main.go @@ -280,6 +280,7 @@ func main() { } r.GET("/getid", server.GetIDHandler) r.POST("/uploadpic", server.UploadBase64ImageHandler(rateLimiter)) + r.POST("/uploadrecord", server.UploadBase64RecordHandler(rateLimiter)) r.Static("/channel_temp", "./channel_temp") //webui和它的api webuiGroup := r.Group("/webui") diff --git a/mylog/mylog.go b/mylog/mylog.go index 64a02273..35084cb1 100644 --- a/mylog/mylog.go +++ b/mylog/mylog.go @@ -184,6 +184,8 @@ type EnhancedLogEntry struct { Message string `json:"message"` } +//todo 下面的不会存入文件,上头的,不会进入ws,改一改 + // 日志频道,所有的 WebSocket 客户端都会在此监听日志事件 var logChannel = make(chan EnhancedLogEntry, 1000) @@ -199,6 +201,12 @@ func Printf(format string, v ...interface{}) { emitLog("INFO", message) } +func Errorf(format string, v ...interface{}) { + log.Printf(format, v...) + message := fmt.Sprintf(format, v...) + emitLog("ERROR", message) +} + func emitLog(level, message string) { entry := EnhancedLogEntry{ Time: time.Now().Format("2006-01-02T15:04:05"), diff --git a/server/uploadpic.go b/server/uploadpic.go index 994e56e6..e0d6c1f3 100644 --- a/server/uploadpic.go +++ b/server/uploadpic.go @@ -108,6 +108,63 @@ func UploadBase64ImageHandler(rateLimiter *RateLimiter) gin.HandlerFunc { } } +// 闭包,网页后端,语音床逻辑,基于gin和www静态文件的简易语音床 +func UploadBase64RecordHandler(rateLimiter *RateLimiter) gin.HandlerFunc { + return func(c *gin.Context) { + ipAddress := c.ClientIP() + if !rateLimiter.CheckAndUpdateRateLimit(ipAddress) { + c.JSON(http.StatusTooManyRequests, gin.H{"error": "rate limit exceeded"}) + return + } + + base64REcord := c.PostForm("base64Record") + // Print the length of the received base64 data + mylog.Println("Received base64 data length:", len(base64REcord), "characters") + + RecordBytes, err := base64.StdEncoding.DecodeString(base64REcord) + if err != nil { + mylog.Println("Error while decoding base64:", err) // Print error while decoding + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid base64 data"}) + return + } + + fileName := generateRandomMd5() + ".silk" + directoryPath := "./channel_temp/" + savePath := directoryPath + fileName + + // Create the directory if it doesn't exist + err = os.MkdirAll(directoryPath, 0755) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "error creating directory"}) + return + } + + err = os.WriteFile(savePath, RecordBytes, 0644) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": "error saving file"}) + return + } + + serverAddress := config.GetServer_dir() + serverPort := config.GetPortValue() + if serverAddress == "" { + // Handle the case where the server address is not configured + c.JSON(http.StatusInternalServerError, gin.H{"error": "server address is not configured"}) + return + } + + // 根据serverPort确定协议 + protocol := "http" + if serverPort == "443" { + protocol = "https" + } + + imageURL := fmt.Sprintf("%s://%s:%s/channel_temp/%s", protocol, serverAddress, serverPort, fileName) + c.JSON(http.StatusOK, gin.H{"url": imageURL}) + + } +} + // 检查是否超过调用频率限制 // 默认1分钟30次 todo 允许用户自行在config编辑限制次数 func (rl *RateLimiter) CheckAndUpdateRateLimit(ipAddress string) bool { diff --git a/silk/silk.go b/silk/silk.go new file mode 100644 index 00000000..cc5a3ae4 --- /dev/null +++ b/silk/silk.go @@ -0,0 +1,119 @@ +//go:build (linux || (windows && !arm && !arm64) || darwin) && (386 || amd64 || arm || arm64) && !race && !nosilk +// +build linux windows,!arm,!arm64 darwin +// +build 386 amd64 arm arm64 +// +build !race +// +build !nosilk + +package silk + +import ( + "crypto/md5" + "encoding/hex" + "errors" + "os" + "os/exec" + "path" + + "github.com/hoshinonyaruko/gensokyo/mylog" + "github.com/wdvxdr1123/go-silk" +) + +const silkCachePath = "data/cache" + +// EncoderSilk 将音频编码为Silk +func EncoderSilk(data []byte) []byte { + h := md5.New() + _, err := h.Write(data) + if err != nil { + mylog.Printf("calc md5 failed") + return nil + } + tempName := hex.EncodeToString(h.Sum(nil)) + slk := encode(data, tempName) + + return slk +} + +// EncodeMP4 将给定视频文件编码为MP4 +func EncodeMP4(src string, dst string) error { + cmd1 := exec.Command("ffmpeg", "-i", src, "-y", "-c", "copy", "-map", "0", dst) + if errors.Is(cmd1.Err, exec.ErrDot) { + cmd1.Err = nil + } + err := cmd1.Run() + if err != nil { + cmd2 := exec.Command("ffmpeg", "-i", src, "-y", "-c:v", "h264", "-c:a", "mp3", dst) + if errors.Is(cmd2.Err, exec.ErrDot) { + cmd2.Err = nil + } + mylog.Printf("convert mp4 failed") + return err + } + return err +} + +// ExtractCover 获取给定视频文件的Cover +func ExtractCover(src string, target string) error { + cmd := exec.Command("ffmpeg", "-i", src, "-y", "-ss", "0", "-frames:v", "1", target) + if errors.Is(cmd.Err, exec.ErrDot) { + cmd.Err = nil + } + mylog.Printf("extract video cover failed") + return nil +} + +// encode 将音频编码为Silk +func encode(record []byte, tempName string) (silkWav []byte) { + // 1. 写入缓存文件 + rawPath := path.Join(silkCachePath, tempName+".wav") + err := os.WriteFile(rawPath, record, os.ModePerm) + if err != nil { + mylog.Errorf("write temp file error") + return nil + } + defer os.Remove(rawPath) + + // 2.转换pcm + pcmPath := path.Join(silkCachePath, tempName+".pcm") + cmd := exec.Command("ffmpeg", "-i", rawPath, "-f", "s16le", "-ar", "24000", "-ac", "1", pcmPath) + if errors.Is(cmd.Err, exec.ErrDot) { + cmd.Err = nil + } + if err = cmd.Run(); err != nil { + mylog.Errorf("convert pcm file error") + return nil + } + defer os.Remove(pcmPath) + + // 3. 转silk + pcm, err := os.ReadFile(pcmPath) + if err != nil { + mylog.Printf("read pcm file err") + return nil + } + silkWav, err = silk.EncodePcmBuffToSilk(pcm, 24000, 24000, true) + if err != nil { + mylog.Printf("silk encode error") + return nil + } + silkPath := path.Join(silkCachePath, tempName+".silk") + err = os.WriteFile(silkPath, silkWav, 0o666) + if err != nil { + mylog.Printf("silk encode error2") + return nil + } + return silkWav +} + +// // resample 将silk重新编码为 24000 bit rate +// func resample(data []byte) []byte { +// pcm, err := silk.DecodeSilkBuffToPcm(data, 24000) +// if err != nil { +// panic(err) +// } +// data, err = silk.EncodePcmBuffToSilk(pcm, 24000, 24000, true) +// if err != nil { +// panic(err) +// } +// return data +// } diff --git a/silk/silk_unsupport.go b/silk/silk_unsupport.go new file mode 100644 index 00000000..1011ecc3 --- /dev/null +++ b/silk/silk_unsupport.go @@ -0,0 +1,16 @@ +//go:build (!arm && !arm64 && !amd64 && !386) || (!windows && !linux && !darwin) || (windows && arm) || (windows && arm64) || race || nosilk +// +build !arm,!arm64,!amd64,!386 !windows,!linux,!darwin windows,arm windows,arm64 race nosilk + +package silk + +import "errors" + +// encode 将音频编码为Silk +func encode(record []byte, tempName string) ([]byte, error) { + return nil, errors.New("not supported now") +} + +// resample 将silk重新编码为 24000 bit rate +func resample(data []byte) []byte { + return data +}