当前位置: 首页 > article >正文

用 Gemini TTS 生成音频故事

我目前正在做一个关于语言学习的副项目。主要功能包括使用 AI 生成内容以及将文本转换为音频文件。为了存储音频文件我还需要云存储。成本是我的首要考虑因素因为我认为在云平台之间切换不会太困难。最终我选择了 Google Gemini、Google TTS 和 Cloudflare R2。它们提供了 API 文档和示例但我发现有些部分不够完善所以我决定写一篇文章来分享。我使用的是 Go 语言本文只涵盖基本用法。对于 Google Gemini 和 TTS我使用 RESTful API。虽然它们提供了库但我发现使用 RESTful API 比设置库更方便。1. Google Geminipackage api import ( bytes encoding/json fmt io net/http github.com/spf13/viper ) type Part struct { Text string json:text } type Content struct { Parts []Part json:parts } type Candidates struct { Content Content json:content } type PromptResult struct { Candidates []Candidates json:candidates } func Prompt(prompt string) (*PromptResult, error) { // I use viper to manage environment variables, you can replace this with your api key. url : fmt.Sprintf(https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key%s, viper.Get(GOOGLE_CLOUD_API_KEY)) // In this example, it sends only one prompt but you can send more information data, err : json.Marshal(map[string]interface{}{ contents: []map[string]interface{}{{ parts: []map[string]interface{}{{ text: prompt, }}, }}, }) if err ! nil { return nil, err } req, err : http.NewRequest(POST, url, bytes.NewBuffer(data)) if err ! nil { return nil, err } req.Header.Add(Content-Type, application/json) // Request the API res, err : http.DefaultClient.Do(req) if err ! nil { return nil, err } defer res.Body.Close() resBody, err : io.ReadAll(res.Body) if err ! nil { return nil, err } var promptResult PromptResult // Parse the result err json.Unmarshal(resBody, promptResult) if err ! nil { return nil, err } return promptResult, nil }package main import ( fmt github.com/hsk-kr/tutorial/lib/api github.com/spf13/viper ) func main() { viper.SetConfigFile(.env) viper.ReadInConfig() promptResult, _ : api.Prompt(Generate a short story for kids) fmt.Println(promptResult.Candidates[0].Content.Parts[0].Text) }您可以在以下链接找到有关 API 的更多详细信息https://ai.google.dev/gemini-api/docs。在此示例中发送了一个提示并接收了响应。响应包含的内容不仅仅是文本但为了简单起见我定义的结构体只处理文本并将其打印到控制台。我使用 Viper 来管理环境变量但您可以通过将 API 密钥替换为您自己的来测试代码。2. Google TTSpackage api import ( bytes encoding/base64 encoding/json errors fmt io net/http strings github.com/spf13/viper ) type Voice struct { LanguageCodes []string json:languageCodes Name string json:name SsmlGender Gender json:ssmlGender // MALE or FEMALE NaturalSampleRateHertz int json:naturalSampleRateHertz } type VoiceSelectionParam struct { LanguageCode string json:languageCode Name string json:name SsmlGender Gender json:ssmlGender } type VoicesResponse struct { Voices []Voice json:voices } type AudioEncoding string type Gender string const ( MALE Gender MALE FEMALE Gender FEMALE ) const ( LINEAR16 AudioEncoding LINEAR16 MP3 AudioEncoding MP3 OGG_OPUS AudioEncoding OGG_OPUS MULAW AudioEncoding MULAW ALAW AudioEncoding ALAW ) func convertVoiceToVoiceSelectionParam(voice Voice) (*VoiceSelectionParam, error) { voiceSelectionParam : new(VoiceSelectionParam) if voice.LanguageCodes nil || len(voice.LanguageCodes) 0 { return nil, errors.New(Empty LanguageCodes) } voiceSelectionParam.LanguageCode voice.LanguageCodes[0] voiceSelectionParam.Name voice.Name voiceSelectionParam.SsmlGender voice.SsmlGender return voiceSelectionParam, nil } func GetVoiceList(languageCode string) ([]Voice, error) { url : fmt.Sprintf(https://texttospeech.googleapis.com/v1/voices?languageCode%skey%s, languageCode, viper.GetString(GOOGLE_CLOUD_API_KEY)) req, err : http.NewRequest(GET, url, nil) if err ! nil { return nil, err } req.Header.Add(Content-Type, application/json) res, err : http.DefaultClient.Do(req) if err ! nil { return nil, err } defer res.Body.Close() resBody, err : io.ReadAll(res.Body) if err ! nil { return nil, err } var voicesRes VoicesResponse err json.Unmarshal(resBody, voicesRes) if err ! nil { return nil, err } return voicesRes.Voices, nil } func ConvertTextToAudio(input string, voice Voice) ([]byte, error) { url : fmt.Sprintf(https://texttospeech.googleapis.com/v1/text:synthesize?key%s, viper.GetString(GOOGLE_CLOUD_API_KEY)) voiceSelectionParam, err : convertVoiceToVoiceSelectionParam(voice) if err ! nil { return nil, err } data, err : json.Marshal(map[string]interface{}{ input: map[string]string{text: input}, voice: voiceSelectionParam, audioConfig: map[string]string{audioEncoding: string(OGG_OPUS)}, }) if err ! nil { return nil, err } req, err : http.NewRequest(POST, url, bytes.NewBuffer(data)) if err ! nil { return nil, err } req.Header.Add(Content-Type, application/json) res, err : http.DefaultClient.Do(req) if err ! nil { return nil, err } defer res.Body.Close() body, err : io.ReadAll(res.Body) if err ! nil { return nil, err } var result map[string]interface{} if err : json.Unmarshal(body, result); err ! nil { return nil, err } audioContent, ok : result[audioContent].(string) if !ok { return nil, fmt.Errorf(No audio content found in response) } audioData, err : base64.StdEncoding.DecodeString(audioContent) if err ! nil { return nil, err } return audioData, nil } func getFirstXVoice(voices []Voice, strToFind string, gender Gender) *Voice { for i, v : range voices { if strings.Contains(strings.ToLower(v.Name), strToFind) v.SsmlGender gender { return voices[i] } } return nil } func GetFirstStandardVoice(voices []Voice, gender Gender) *Voice { return getFirstXVoice(voices, standard, gender) } func GetFirstWavenetVoice(voices []Voice, gender Gender) *Voice { return getFirstXVoice(voices, wavenet, gender) } func GetFirstNeuralVoice(voices []Voice, gender Gender) *Voice { return getFirstXVoice(voices, neural, gender) }package main import ( fmt os github.com/hsk-kr/tutorial/lib/api github.com/spf13/viper ) func main() { viper.SetConfigFile(.env) viper.ReadInConfig() voices, _ : api.GetVoiceList(en-US) voice : api.GetFirstWavenetVoice(voices, MALE) bAudio, _ : api.ConvertTextToAudio(By the way, I am using neovim., *voice) os.WriteFile(./audio.opus, bAudio, 0644) }运行程序后您会在同一目录下找到名为 audio.opus 的音频文件。共有三个函数GetVoiceList– 获取 API 支持的语音列表。GetFirstWavenetVoice– 截至 2025 年 2 月 27 日共有三种类型的语音wavenet、standard 和 neural。每种类型包含多个语音但由于这不是我的优先考虑我创建了一个函数来简单地获取给定类型的第一个语音。ConvertTextToAudio– 以文本和语音作为参数返回结果为 []byte。该函数请求 OGG_OPUS 格式的音频文件因为我计划在 Web 环境中使用它。但是您可以使用任何支持的格式。如果您仔细阅读文档可能会发现它缺少一些重要的细节。例如我遇到了 audio_encoding 参数想检查支持哪些格式但没有找到直接链接到该信息的页面。我认为文档可以改进——当我搜索音频编码文档的链接时一无所获只有纯黑色文本。最终我通过在文档顶部手动搜索找到了该文档。3. Cloudflare R2package api import ( bytes context errors fmt time github.com/aws/aws-sdk-go-v2/aws github.com/aws/aws-sdk-go-v2/config github.com/aws/aws-sdk-go-v2/credentials github.com/aws/aws-sdk-go-v2/feature/s3/manager github.com/aws/aws-sdk-go-v2/service/s3 github.com/google/uuid github.com/spf13/viper ) type Storage struct { client *s3.Client uploader *manager.Uploader bucketName string } func (s *Storage) Init() error { cfg, err : config.LoadDefaultConfig(context.TODO(), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(viper.GetString(CLOUDFLARE_R2_ACCESS_KEY_ID), viper.GetString(CLOUDFLARE_R2_SECRET_ACCESS_KEY), )), config.WithRegion(auto), ) if err ! nil { return err } s.bucketName viper.GetString(CLOUDFLARE_R2_BUCKET_NAME) s.client s3.NewFromConfig(cfg, func(o *s3.Options) { o.BaseEndpoint aws.String(fmt.Sprintf(https://%s.r2.cloudflarestorage.com, viper.GetString(CLOUDFLARE_R2_ACCOUNT_ID))) }) s.uploader manager.NewUploader(s.client) return nil } func (s *Storage) Put(data []byte) (string, error) { if s.client nil { return , errors.New(client is nil) } objectKey, err : uuid.NewUUID() if err ! nil { return , err } bucket : aws.String(s.bucketName) key : aws.String(objectKey.String()) ctx : context.Background() input : s3.PutObjectInput{ Bucket: bucket, Key: key, Body: bytes.NewReader(data), } output, err : s.uploader.Upload(ctx, input) if err ! nil { return , err } err s3.NewObjectExistsWaiter(s.client).Wait(ctx, s3.HeadObjectInput{ Bucket: bucket, Key: key, }, time.Minute) if err ! nil { return , err } return *output.Key, nil }package main import ( github.com/tutorial/justsayit/lib/api github.com/spf13/viper ) func main() { viper.SetConfigFile(.env) viper.ReadInConfig() voices, _ : api.GetVoiceList(en-US) voice : api.GetFirstWavenetVoice(voices, MALE) bAudio, _ : api.ConvertTextToAudio(By the way, I am using neovim., *voice) storage : new(api.Storage) storage.Init() storage.Put(bAudio) }Cloudflare R2 类似于 Amazon S3因此您可以使用 AWS S3 库来操作其 API。您可以在 AWS 文档中找到更多示例https://docs.aws.amazon.com/code-library/latest/ug/go_2_s3_code_examples.html。由于在将对象上传到云端之前需要创建一些实例我在结构体中定义了必要的方法。运行程序后您应该能看到对象成功上传到 Cloudflare。为了测试音频文件是否能在 Web 环境中正确加载我将对象 URL 放在了标签中——但没有起作用。原来即使我已将文件设置为公开访问仍然需要使用代理来访问它。从安全角度来看这是合理的因为必须在设置中明确允许访问。如果您想临时访问文件进行测试可以启用开发模式并使用开发链接。4. 最终代码package main import ( github.com/tutorial/justsayit/lib/api github.com/spf13/viper ) func main() { viper.SetConfigFile(.env) viper.ReadInConfig() promptResult, _ : api.Prompt(Say something short in German) generatedText : promptResult.Candidates[0].Content.Parts[0].Text voices, _ : api.GetVoiceList(de-DE) voice : api.GetFirstWavenetVoice(voices, MALE) bAudio, _ : api.ConvertTextToAudio(generatedText, *voice) storage : new(api.Storage) storage.Init() storage.Put(bAudio) }这就是最终代码使用 Google Gemini 生成文本将文本转换为音频文件并将音频文件存储在云平台中。原文链接用 Gemini TTS 生成音频故事 - 汇智网

相关文章:

用 Gemini TTS 生成音频故事

我目前正在做一个关于语言学习的副项目。主要功能包括使用 AI 生成内容以及将文本转换为音频文件。为了存储音频文件,我还需要云存储。 成本是我的首要考虑因素,因为我认为在云平台之间切换不会太困难。 最终,我选择了 Google Gemini、Goog…...

佳能TS6320、TS8320、MG3680、G3800 G3810 G6080 TS3380、G3000、ts3440、ip6700错误代码5b00,p07,e08,1700解决方法,用软件清零即可

下载:点这里下载 备用下载:https://pan.baidu.com/s/1WrPFvdV8sq-qI3_NgO2EvA?pwd0000 常见型号如下: G系列 G1000、G1100、G1200、G1400、G1500、G1800、G1900、G1010、G1110、G1120、G1410、G1420、G1411、G1510、G1520、G1810、G1820、…...

TI CCS在Win10安装卡壳?手把手教你排查并修复‘临时路径Unicode字符’问题(附注册表安全修改指南)

TI CCS在Win10安装卡壳?深度解析Unicode路径问题与系统级解决方案 当你在Windows 10系统上安装TI Code Composer Studio(CCS)时,是否遇到过这样的报错提示:"Your temp directory path contains Unicode characte…...

抖音内容采集全栈解决方案:从单视频到批量管理的技术实践

抖音内容采集全栈解决方案:从单视频到批量管理的技术实践 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback su…...

WindowsCleaner:当C盘告急时,我是如何从手动清理到自动化专家的

WindowsCleaner:当C盘告急时,我是如何从手动清理到自动化专家的 【免费下载链接】WindowsCleaner Windows Cleaner——专治C盘爆红及各种不服! 项目地址: https://gitcode.com/gh_mirrors/wi/WindowsCleaner 那天下午,我正…...

AzurLaneAutoScript终极指南:快速掌握碧蓝航线全自动脚本

AzurLaneAutoScript终极指南:快速掌握碧蓝航线全自动脚本 【免费下载链接】AzurLaneAutoScript Azur Lane bot (CN/EN/JP/TW) 碧蓝航线脚本 | 无缝委托科研,全自动大世界 项目地址: https://gitcode.com/gh_mirrors/az/AzurLaneAutoScript 还在为…...

# 030、AutoSAR AP实战:配置执行管理与应用生命周期

一、从一次诡异的进程启动失败说起 上周在测试环境里碰到一个怪事:某个功能服务进程在系统启动后总是超时退出,日志里只有一句冷冰冰的 APPLICATION_STARTUP_TIMEOUT。查了半小时才发现,不是代码逻辑问题,而是执行管理(Execution Management, EM)里配置的启动超时时间被…...

2026 费控管理系统排行榜:这 10 款软件深受名企青睐

本文将深入对比10款费用管理系统:合思(易快报)、胜意费控云、泛微齐业成费控系统、航天信息财智云费控系统、汇联易费控系统、鼎捷云费控、致远费控、每刻报销、Zoho Expense、分贝通 企业数字化转型的深水区,传统的纸质报销和粗放…...

Wan2.2-I2V-A14B入门:JDK1.8环境下的Java SDK开发与调用示例

Wan2.2-I2V-A14B入门:JDK1.8环境下的Java SDK开发与调用示例 1. 环境准备与快速部署 如果你所在的企业仍在使用JDK 1.8,这篇教程将带你快速集成Wan2.2-I2V-A14B模型。这个AI模型能够将图片转换为视频,在电商、内容创作等领域有广泛应用。我…...

别再只盯着FPS了!聊聊IA-SSD在RTX 2080Ti上85帧背后的显存与并行性玄学

解码IA-SSD的85帧神话:当点云检测遇上显存优化的技术博弈 在自动驾驶和机器人领域,每秒85帧的3D目标检测性能听起来像是一个梦幻般的数字。IA-SSD论文中这个耀眼的FPS数据,让不少工程师第一反应是"这显卡怕不是装了涡轮增压器"。但…...

终极指南:八大网盘直链下载助手,告别限速烦恼的完整教程

终极指南:八大网盘直链下载助手,告别限速烦恼的完整教程 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移…...

三步解锁Beyond Compare 5完整功能:免费密钥生成器终极指南

三步解锁Beyond Compare 5完整功能:免费密钥生成器终极指南 【免费下载链接】BCompare_Keygen Keygen for BCompare 5 项目地址: https://gitcode.com/gh_mirrors/bc/BCompare_Keygen 还在为Beyond Compare 5的30天评估期到期而烦恼吗?想要免费获…...

opencode AI 编码代理在公司级、私有化的表现到底如何

OpenCode 是一个开源的 AI 编码代理。它提供终端界面、桌面应用和 IDE 扩展等多种使用方式。如果我们想在公司级使用,在确保数据、代码安全的前提下,它是目前最优解之一,支持的插件足够多,速度还可以,速度取决于模型的…...

大麦网Python抢票脚本终极指南:告别黄牛,轻松抢到心仪门票

大麦网Python抢票脚本终极指南:告别黄牛,轻松抢到心仪门票 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 还在为抢不到演唱会门票而烦恼吗?每次开票秒光&…...

Qwen3-VL-8B开源镜像部署教程:免手动配置vLLM端口与代理转发规则

Qwen3-VL-8B开源镜像部署教程:免手动配置vLLM端口与代理转发规则 1. 引言:为什么你需要这个开箱即用的AI聊天系统? 如果你曾经尝试过部署一个大型语言模型,大概率会遇到这样的麻烦:模型服务启动后,端口怎…...

游戏模组支持脚本扩展与资源替换

游戏模组支持脚本扩展与资源替换:开启无限创意之门 在游戏开发与玩家社区中,模组(Mod)一直是推动游戏生命力延续的核心动力之一。通过支持脚本扩展与资源替换,游戏模组不仅能够改变游戏的外观和玩法,还能为…...

LinkSwift技术解析:八大网盘直链获取方案与架构设计深度分析

LinkSwift技术解析:八大网盘直链获取方案与架构设计深度分析 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 …...

突破性解决方案:FastbootEnhance如何3步解决Android设备管理的复杂技术挑战

突破性解决方案:FastbootEnhance如何3步解决Android设备管理的复杂技术挑战 【免费下载链接】FastbootEnhance A user-friendly Fastboot ToolBox & Payload Dumper for Windows 项目地址: https://gitcode.com/gh_mirrors/fa/FastbootEnhance FastbootE…...

爱毕业aibiye具备每日免费无限查重功能,集成AI改写工具,帮助用户轻松调整论文内容

核心工具对比速览 工具名称 查重速度 降重效果 特色功能 适用场景 aicheck 极快 重复率可降30% 专业术语保留 高重复率紧急处理 aibiye 中等 逻辑优化明显 学术表达增强 提升论文质量 askpaper 快 结构保持完整 多语言支持 外文论文降重 秒篇 极快 上下文…...

Qwen2.5-VL-7B-Instruct在智能导航系统中的应用:牢记回家路

Qwen2.5-VL-7B-Instruct在智能导航系统中的应用:牢记回家路 想象一下,当你开车回家时,导航系统不仅能告诉你该走哪条路,还能认出你常去的超市、记得你喜欢的咖啡店,甚至提醒你:"今天常去的那家花店有新…...

Qwen3-4B-Thinking效果展示:对比原版Qwen3-4B,推理链准确率提升实测

Qwen3-4B-Thinking效果展示:对比原版Qwen3-4B,推理链准确率提升实测 1. 模型介绍与核心升级 1.1 基础架构与特性 Qwen3-4B-Thinking-2507-Gemini-2.5-Flash-Distill是基于通义千问Qwen3-4B官方模型的升级版本,保留了原版4B参数规模的稠密(…...

中兴光猫专业管理工具:zteOnu 工厂模式与Telnet一键开启实战指南

中兴光猫专业管理工具:zteOnu 工厂模式与Telnet一键开启实战指南 【免费下载链接】zteOnu A tool that can open ZTE onu device factory mode 项目地址: https://gitcode.com/gh_mirrors/zt/zteOnu zteOnu是一款专为中兴光猫设备设计的高效管理工具&#xf…...

黑丝空姐-造相Z-Turbo快速上手:3步完成模型部署与图片生成

黑丝空姐-造相Z-Turbo快速上手:3步完成模型部署与图片生成 1. 环境准备与快速部署 1.1 系统要求与准备工作 在开始部署前,请确保您的环境满足以下基本要求: 操作系统:Linux(推荐Ubuntu 20.04/22.04)或W…...

用2美元的Attiny85芯片DIY一个USB键盘:手把手教你玩转Digispark(附完整代码)

用2美元的Attiny85打造智能USB外设:从键盘到创意交互的全能开发指南 当一块比指甲盖还小的芯片能够模拟键盘输入、控制鼠标光标甚至实现自定义HID设备时,硬件开发的边界就被彻底打破了。Attiny85这颗售价仅2美元的8引脚微控制器,配合Digispar…...

低速机器人路径跟踪选MPC还是PID?从双轮差速模型实战看优劣与适用场景

低速机器人路径跟踪:MPC与PID的深度对比与工程选型指南 当你的双轮差速机器人需要在仓库货架间精准穿梭,或是服务机器人在餐厅里灵活避让桌椅时,控制算法的选择直接决定了项目成败。作为经历过7个机器人项目的技术负责人,我见过太…...

**RISC-V生态下的轻量级嵌入式操作系统开发实战:从零构建你的第一个RTOS内核**

RISC-V生态下的轻量级嵌入式操作系统开发实战:从零构建你的第一个RTOS内核 在当前国产化替代浪潮中,RISC-V架构正成为嵌入式系统开发的新高地。相比传统ARM架构,RISC-V以其开源、模块化、可定制等优势,吸引了大量开发者和企业投入…...

# 发散创新:基于A*算法的AI寻路优化实战与多场景适配在游戏开发、机器人导航和自动驾驶等领域,**智能寻路系统**是

发散创新:基于A*算法的AI寻路优化实战与多场景适配 在游戏开发、机器人导航和自动驾驶等领域,智能寻路系统是核心模块之一。传统BFS/DFS方法虽然简单,但在复杂地图中效率低下;而A*(A-Star)算法凭借启发式函…...

3秒完成图片格式转换:Save Image as Type终极效率指南

3秒完成图片格式转换:Save Image as Type终极效率指南 【免费下载链接】Save-Image-as-Type Save Image as Type is an chrome extension which add Save as PNG / JPG / WebP to the context menu of image. 项目地址: https://gitcode.com/gh_mirrors/sa/Save-I…...

超越官方控制面板:NVIDIA Profile Inspector如何解锁显卡隐藏潜力?

超越官方控制面板:NVIDIA Profile Inspector如何解锁显卡隐藏潜力? 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector 你是否曾对NVIDIA官方控制面板感到束手束脚?那些看…...

暴雨“钉”在桂北第6天:兴安296mm特大暴雨,桂林柳州风电场正在经历什么?

4月的广西,本不该这样下雨。过去几天,一条强降雨带稳稳盘踞在桂北一带,桂林、柳州相继出现成片的大暴雨区。桂林兴安县更是下出了296毫米的特大暴雨——相当于一天之内把北京半年的雨量倒在了这座县城。广西气象台的预报显示,这场…...