From 9c3ee32098620c655c2840e765c2d152d2a75194 Mon Sep 17 00:00:00 2001 From: bing Date: Mon, 30 Dec 2024 16:47:21 +0800 Subject: [PATCH] =?UTF-8?q?feat=20=E6=B7=BB=E5=8A=A0ogg=E8=BD=ACmp3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- go.mod | 1 + go.sum | 2 + go/ogg2mp3/CMakeLists.txt | 23 ++++++++++ go/ogg2mp3/main.go | 97 +++++++++++++++++++++++++++++++++++++++ go/ogg2mp3/ogg2mp3.cpp | 77 +++++++++++++++++++++++++++++++ go/ogg2mp3/ogg2mp3.h | 7 +++ 7 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 go/ogg2mp3/CMakeLists.txt create mode 100644 go/ogg2mp3/main.go create mode 100644 go/ogg2mp3/ogg2mp3.cpp create mode 100644 go/ogg2mp3/ogg2mp3.h diff --git a/.gitignore b/.gitignore index 92b1626..dd9b8cf 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,6 @@ .vscode *.log -go/bin \ No newline at end of file +go/bin + +build/ diff --git a/go.mod b/go.mod index 499d1e3..f22f7ed 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/bogem/id3v2/v2 v2.1.4 github.com/coreos/go-oidc/v3 v3.1.0 github.com/gin-gonic/gin v1.8.1 + github.com/ianlancetaylor/cgosymbolizer v0.0.0-20241129212102-9c50ad6b591e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/spf13/cobra v1.7.0 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 diff --git a/go.sum b/go.sum index a5d6b2c..60dac96 100644 --- a/go.sum +++ b/go.sum @@ -198,6 +198,8 @@ github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:Fecb github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/cgosymbolizer v0.0.0-20241129212102-9c50ad6b591e h1:8AnObPi8WmIgjwcidUxaREhXMSpyUJeeSrIkZTXdabw= +github.com/ianlancetaylor/cgosymbolizer v0.0.0-20241129212102-9c50ad6b591e/go.mod h1:DvXTE/K/RtHehxU8/GtDs4vFtfw64jJ3PaCnFri8CRg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= diff --git a/go/ogg2mp3/CMakeLists.txt b/go/ogg2mp3/CMakeLists.txt new file mode 100644 index 0000000..410e62a --- /dev/null +++ b/go/ogg2mp3/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.10) +project(ogg2mp3) + +set(CMAKE_CXX_STANDARD 11) + +# 手动指定 Vorbis 和 MP3Lame 库的路径 +set(VORBIS_INCLUDE_DIRS /usr/include) +set(VORBIS_LIBRARIES /usr/lib/x86_64-linux-gnu/libvorbisfile.so /usr/lib/x86_64-linux-gnu/libvorbis.so /usr/lib/x86_64-linux-gnu/libogg.so) +set(MP3LAME_INCLUDE_DIRS /usr/include) +set(MP3LAME_LIBRARIES /usr/lib/x86_64-linux-gnu/libmp3lame.so) + +# 包含头文件目录 +include_directories(${VORBIS_INCLUDE_DIRS}) +include_directories(${MP3LAME_INCLUDE_DIRS}) + +# 添加源文件 +set(SOURCES ogg2mp3.cpp) + +# 创建静态库 +add_library(ogg2mp3 STATIC ${SOURCES}) + +# 链接库 +target_link_libraries(ogg2mp3 ${VORBIS_LIBRARIES} ${MP3LAME_LIBRARIES}) \ No newline at end of file diff --git a/go/ogg2mp3/main.go b/go/ogg2mp3/main.go new file mode 100644 index 0000000..23d46ee --- /dev/null +++ b/go/ogg2mp3/main.go @@ -0,0 +1,97 @@ +package main + +/* +#cgo LDFLAGS: -L${SRCDIR}/build -logg2mp3 -lvorbisfile -lvorbis -logg -lmp3lame -lstdc++ +#include +#include "ogg2mp3.h" +*/ +import "C" +import ( + "log" + "os" + "path/filepath" + "strings" + "unsafe" + + _ "github.com/ianlancetaylor/cgosymbolizer" // cgo 错误跟踪 + "github.com/spf13/cobra" +) + +func convertOggToMp3(src string, dst string) C.int { + input := C.CString(src) + defer C.free(unsafe.Pointer(input)) + output := C.CString(dst) + defer C.free(unsafe.Pointer(output)) + return C.convertOggToMp3(input, output) +} +// listOggFiles 遍历指定文件夹,返回所有 OGG 文件的路径列表 +func listOggFiles(dir string) ([]string, error) { + var oggFiles []string + err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".ogg") { + oggFiles= append(oggFiles, path) + } + return nil + }) + return oggFiles, err +} + +// convertOggFilesToMp3 将指定文件夹下的所有 OGG 文件转换为 MP3 +func convertOggFilesToMp3(files []string, outputDir string) { + for _, file := range files { + outputFile := filepath.Join(outputDir, strings.TrimSuffix(filepath.Base(file), ".ogg")+".mp3") + if n := convertOggToMp3(file, outputFile); n != 0 { + log.Printf("Error converting %s to %s: %d", file, outputFile, n) + } else { + log.Printf("Successfully converted %s to %s", file, outputFile) + } + } +} + + +func main() { + var rootCmd = &cobra.Command{Use: "main"} + var convertCmd = &cobra.Command{ + Use: "file [input.ogg] [output.mp3]", + Short: "Convert OGG to MP3", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + input := C.CString(args[0]) + defer C.free(unsafe.Pointer(input)) + output := C.CString(args[1]) + defer C.free(unsafe.Pointer(output)) + if n := C.convertOggToMp3(input, output); n != 0 { + log.Fatalf("Error converting OGG to MP3: %d", n) + } + }, + } + var convertDirCmd = &cobra.Command{ + Use: "dir [inputDir] [outputDir]", + Short: "Convert all OGG files in a directory to MP3", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + inputDir := args[0] + outputDir := inputDir + "_mp3" + if len(args) == 2 { + outputDir = args[1] + } + if _, err := os.Stat(outputDir); os.IsNotExist(err) { + if err := os.MkdirAll(outputDir, 0755); err != nil { + log.Fatalf("Error creating output directory: %v", err) + } + } + oggFiles, err := listOggFiles(inputDir) + if err != nil { + log.Fatalf("Error listing OGG files: %v", err) + } + + convertOggFilesToMp3(oggFiles, outputDir) + }, + } + + rootCmd.AddCommand(convertCmd, convertDirCmd) + rootCmd.Execute() +} diff --git a/go/ogg2mp3/ogg2mp3.cpp b/go/ogg2mp3/ogg2mp3.cpp new file mode 100644 index 0000000..9182605 --- /dev/null +++ b/go/ogg2mp3/ogg2mp3.cpp @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include "ogg2mp3.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define OGG_FILE_ERROR 1 +#define MP3_FILE_ERROR 2 +#define OGG_DECODE_ERROR 3 +#define MP3_ENCODE_ERROR 4 + +int convertOggToMp3(const char* oggFilePath, const char* mp3FilePath) { + // 打开 OGG 文件 + FILE* oggFile = fopen(oggFilePath, "rb"); + if (!oggFile) { + return OGG_FILE_ERROR; + } + + // 打开 MP3 文件 + FILE* mp3File = fopen(mp3FilePath, "wb"); + if (!mp3File) { + fclose(oggFile); + return MP3_FILE_ERROR; + } + + // 初始化 OGG 解码器 + OggVorbis_File vf; + if (ov_open(oggFile, &vf, NULL, 0) < 0) { + fclose(oggFile); + fclose(mp3File); + return OGG_DECODE_ERROR; + } + + // 获取音频信息 + vorbis_info* vi = ov_info(&vf, -1); + + // 初始化 LAME 编码器 + lame_t lame = lame_init(); + lame_set_in_samplerate(lame, vi->rate); + lame_set_num_channels(lame, vi->channels); + lame_set_VBR(lame, vbr_default); + lame_init_params(lame); + + // 缓冲区 + const int BUFFER_SIZE = 4096; + short int pcm_buffer[BUFFER_SIZE * vi->channels]; + unsigned char mp3_buffer[BUFFER_SIZE]; + + int read, write; + int current_section; + + // 读取 OGG 数据并编码为 MP3 + while ((read = ov_read(&vf, (char*)pcm_buffer, sizeof(pcm_buffer), 0, 2, 1, ¤t_section)) > 0) { + write = lame_encode_buffer_interleaved(lame, pcm_buffer, read / (2 * vi->channels), mp3_buffer, BUFFER_SIZE); + fwrite(mp3_buffer, write, 1, mp3File); + } + + // 结束编码 + write = lame_encode_flush(lame, mp3_buffer, BUFFER_SIZE); + fwrite(mp3_buffer, write, 1, mp3File); + + // 清理 + lame_close(lame); + ov_clear(&vf); + fclose(mp3File); + // fclose(oggFile); + + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/go/ogg2mp3/ogg2mp3.h b/go/ogg2mp3/ogg2mp3.h new file mode 100644 index 0000000..d234f9c --- /dev/null +++ b/go/ogg2mp3/ogg2mp3.h @@ -0,0 +1,7 @@ +#ifdef __cplusplus +extern "C" { +#endif +int convertOggToMp3(const char* oggFilePath, const char* mp3FilePath); +#ifdef __cplusplus +} +#endif