由播客转向个人定制的音频频道(1)平台搭建

项目的背景

        最近开始听喜马拉雅播客的内容,但是发现许多不方便的地方。

  •         休息的时候收听喜马拉雅,但是还需要不断地选择喜马拉雅的内容,比较麻烦,而且黑灯操作反而伤眼睛。
  •       喜马拉雅为代表的播客平台都是VOD 形式的,需要选择内容收听,有时候想听科技方面的访谈,但是许多访谈节目并不是连续更新播放的。免不了要去主动查找。
  •       休闲或者开车时,希望收听收音机那样轻松一点地享受电台安排的节目,但是目前的电台广告太多,内容匮乏。
  • 家里的老人更不习惯手机操作。老人目前主要是看短视频。

   笔者看来,播客是一个被低估的服务,其实依靠短视频很难接收有效的信息,靠几分钟很难讲清楚一个观点和知识。所以,要完整地了解一些有用的内容,语音比短视频更好。

        那么,能否通过AI 推荐技术,讲播客内容主动生成个人定制的音频频道吗?理论上是可能的,也十分有趣。作为一名创客,我想试试。

        说干就干!本文介绍实验平台的搭建。

基于ardunio ESP32 的选台器  

          电子设备中经常使用旋钮来选择参数,最简单的是旋钮是电位器,它是一个滑动电阻,高端家电,汽车中使用的是编码器Encoder。编码器输出的是脉冲信号。本文介绍如何使用Ardunio ESP32-Nano 来设计一个蓝牙旋转编码器。

外观设计

硬件设计

细节

编码器

下面是日本ALPS 公司的中空旋转编码器EC35A。

 

三个引脚分别是A,C,B。

 

接线图

最好在编码器脉冲计数处理回路中设置下图所示的滤波器。

与Ardunio 的连接图。 

代码

目前的代码使用了蓝牙mouse 仿真,最终的程序也许要改成ble server 。

#include <BleMouse.h>
BleMouse bleMouse;
// Define the pins used for the encoder
const int encoderPinA = 11;
const int encoderPinB = 10;// Variables to keep the current and last state
volatile int encoderPosCount = 0;
int lastEncoded = 0;
int MAX=60;
int channel=-1;
void setup() {Serial.begin(115200);// Set encoder pins as input with pull-up resistorspinMode(encoderPinA, INPUT_PULLUP); pinMode(encoderPinB, INPUT_PULLUP);// Attach interrupts to the encoder pinsattachInterrupt(digitalPinToInterrupt(encoderPinA), updateEncoder, CHANGE);attachInterrupt(digitalPinToInterrupt(encoderPinB), updateEncoder, CHANGE);bleMouse.begin();
}void loop() {static int lastReportedPos = -1; // Store the last reported positionif (encoderPosCount != lastReportedPos) {if((encoderPosCount/2)>channel) {channel++; Serial.print("Encoder Position: ");Serial.println(channel);bleMouse.move(0,0,1);};if((encoderPosCount/2)<channel) {channel--; Serial.print("Encoder Position: ");Serial.println(channel);bleMouse.move(0,0,-1);}lastReportedPos = encoderPosCount;}delay(500) ;}void updateEncoder() {int MSB = digitalRead(encoderPinA); // MSB = most significant bitint LSB = digitalRead(encoderPinB); // LSB = least significant bitint encoded = (MSB << 1) | LSB; // Converting the 2 pin value to single numberint sum  = (lastEncoded << 2) | encoded; // Adding it to the previous encoded valueif(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011){if (encoderPosCount==MAX) encoderPosCount=0;elseencoderPosCount++;} if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000){if (encoderPosCount==0) encoderPosCount=MAX;elseencoderPosCount--;} lastEncoded = encoded; // Store this value for next time
}

 ardunio-ESP32-nano 开发板

外观

引脚图 

HLS 网络电台的构建

        HLS 全称是 HTTP Live Streaming, 是一个由 Apple 公司实现的基于 HTTP 的媒体流传输协议。  借助 HLS,视频和音频内容被分解为一系列块,经过压缩以便快速交付,并通过 HTTP 传输到最终用户的设备。

 HLS 由一个m3u8 文件和多个ts 文件构成的。 

节目源

网络上有许多网络广播电台的m3u8 的节目源地址,有的可以播放,有的不行。我们下载之后转换成为CSV 格式,然后通过CSV2JSON.js 软件转化为json 文件.下面是一部分

[{"StationName": "本地音乐台","URL": "media/1.m3u8"},{"StationName": "CGTN Radio","URL": "http://sk.cri.cn/am846.m3u8"},{"StationName": "CRI环球资讯广播","URL": "http://satellitepull.cnr.cn/live/wxhqzx01/playlist.m3u8"},{"StationName": "CRI华语环球广播","URL": "http://sk.cri.cn/hyhq.m3u8"},{"StationName": "CRI南海之声","URL": "http://sk.cri.cn/nhzs.m3u8"},{"StationName": "CRI世界华声","URL": "http://sk.cri.cn/hxfh.m3u8"}]

 音频分发服务器

      构建了一个HLS 音频测试平台,用于测试。

  •           后台nodeJS 编写
  •          前端 使用hlv.js 插件

自制HLS 媒体

除了网络上的节目源之外,我们也制作了一些测试语音媒体。要使用ffmpeg 工具转换。 

使用ffmpeg 将mp3 转换成m3u8 的分段(1.mp3 )

ffmpeg -i 1.mp3 -c:v libx264 -c:a aac -strict -2 -f hls -hls_list_size 2 -hls_time 15 1.m3u8

生成的效果是:

        将 1.mp3 视频文件每 15 秒生成一个 ts 文件,最后生成一个 m3u8 文件(1.m3u8),m3u8 文件是 ts 的索引文件。和两个ts( 1.m3u8 ,10.ts 和11.ts)

将生成的文件放置在nodeJS/public/media 目录中。

我的代码 

NodeJS代码

import express from 'express';
import path from 'path'
import url from 'url'
import   fs   from 'fs';
const router = express.Router();
const app = express();
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json())router.get('/index', function (req, res) {res.sendFile(path.join(__dirname + '/views/index.html'));
});
router.post('/getStations', async function (req, res) {Request = req.body;//   console.log(Request)const Method = Request.Method;const Stations=JSON.parse(fs.readFileSync("public/doc/StationTable.json",'utf8'))console.log(Stations)res.send(JSON.stringify({Method: "getStations",Result: { Status: "OK", Stations: JSON.stringify(Stations) }}))
});
app.use('/', router);
app.listen(process.env.port || 3000);
console.log('Running at Port 3000');

前端Index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Radio Player</title><link rel="stylesheet" href="css/bootstrap.min.css"><link rel="stylesheet" href="css/font-awesome.min.css"><link rel="stylesheet" href="font/bootstrap-icons.css"><script src="js/jquery-3.4.1.min.js"></script><script src="js/bootstrap.min.js"></script><script src="js/hls.js"></script><script src="js/jquery.mousewheel.min.js"></script><style>select {font-size: 24px;width: 320px;}audio {width: 320px;}</style><script>var hlsvar CurrentChannel=0$(document).ready(function () {$("#Title").click()var audio = document.getElementById('audio');hls = new Hls();$("#Title").mousewheel(Mousewheel)         getStations()});var Stations = nullfunction Mousewheel(event){console.log(event.deltaX, event.deltaY, event.deltaFactor);if (event.deltaY>0) {CurrentChannel++;if (CurrentChannel==64) CurrentChannel==0}else  {if (CurrentChannel==0) CurrentChannel=64elseCurrentChannel--;}$("#Selection").get(0).selectedIndex=CurrentChannelSelected()}function getStations() {var parameter = {Method: "getStations",}$.ajax({url: "/getStations",type: 'post',contentType: "application/json",dataType: "json",data: JSON.stringify(parameter),success: function (response) {Stations = JSON.parse(response.Result.Stations)console.log(Stations)for (let i = 0; i < Stations.length; i++) {$("#Selection").append("<option>" + Stations[i].StationName + "</option>")}$("#Selection").get(0).selectedIndex=CurrentChannelSelected()}})}function Selected() {const StationName = $("#Selection").val()const Index = $("#Selection").get(0).selectedIndexCurrentChannel=Indexconsole.log(StationName)console.log(Index)if (Hls.isSupported()) {var audio = document.getElementById('audio');hls.loadSource(Stations[Index].URL);hls.attachMedia(audio);hls.on(Hls.Events.MANIFEST_PARSED, function () {audio.play();});}}</script></head><body><div class="container" ><h1 class="text-info" id="Title">网络收音机</h1><div class="form-group"><h3 class="text-info">选台</h3><select class="form-select   " aria-label="Default select" id="Selection" onchange="Selected()"></select></div><div><h3 class="text-info">播放器</h3><audio id="audio" controls ></audio></div></div>
</body></html>

界面有点丑

结束语

下一步,开始研究个人定制的音频频道的构建和尝试。感兴趣的可以共同探讨。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/61044.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k-近邻算法(K-Nearest Neighbors, KNN)详解:机器学习中的经典算法

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

pycharm分支提交操作

一、Pycharm拉取Git远程仓库代码 1、点击VCS > Get from Version Control 2、输入git的url&#xff0c;选择自己的项目路径 3、点击Clone&#xff0c;就拉取成功了 默认签出分支为main 选择develop签出即可进行开发工作 二、创建分支&#xff08;非必要可以不使用&#xf…

PySpark——Python与大数据

一、Spark 与 PySpark Apache Spark 是用于大规模数据&#xff08; large-scala data &#xff09;处理的统一&#xff08; unified &#xff09;分析引擎。简单来说&#xff0c; Spark 是一款分布式的计算框架&#xff0c;用于调度成百上千的服务器集群&#xff0c;计算 TB 、…

算法日记 26-27day 贪心算法

接下来的题目有些地方比较相似。需要注意多个条件。 题目&#xff1a;分发糖果 135. 分发糖果 - 力扣&#xff08;LeetCode&#xff09; n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 每…

Python绘制雪花

文章目录 系列目录写在前面技术需求完整代码代码分析1. 代码初始化部分分析2. 雪花绘制核心逻辑分析3. 窗口保持部分分析4. 美学与几何特点总结 写在后面 系列目录 序号直达链接爱心系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4…

【一键整合包及教程】AI照片数字人工具EchoMimic技术解析

在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度改变着我们的生活。EchoMimic&#xff0c;作为蚂蚁集团旗下支付宝推出的开源项目&#xff0c;不仅为数字人技术的发展掀开了新的一页&#xff0c;更为娱乐、教育、虚拟现实、在线会议等多个领域带…

STM32中断系统

目录 一、中断的基本概念 二、NVIC 1.NVIC的概念 2、NVIC的组成 3、NVIC的应用 4.NVIC的结构 三、外部中断EXTI 1.外部中断的概念 2.EXTI基本结构 四、EXTI外部中断的配置流程 1.开启APB2中的GPIO口/AFIO时钟 2.GPIO配置成输入模式 3.AFIO选择中断引脚 4.EXTI初始…

解锁远程AI工作流:Flowise搭配cpolar跨地域管理AI项目

文章目录 前言1. Docker安装Flowise2. Ubuntu安装Cpolar3. 配置Flowise公网地址4. 远程访问Flowise5. 固定Cpolar公网地址6. 固定地址访问 前言 如今&#xff0c;工作流自动化与人工智能的结合已成为提升生产力的重要手段。Flowise正是这样一个工具&#xff0c;通过直观的拖拽…

Cyberchef配合Wireshark提取并解析HTTP/TLS流量数据包中的文件

本文将介绍一种手动的轻量级的方式&#xff0c;还原HTTP/TLS协议中传输的文件&#xff0c;为流量数据包中的文件分析提供帮助。 如果捕获的数据包中存在非文本类文件&#xff0c;例如png,jpg等图片文件&#xff0c;或者word&#xff0c;Excel等office文件异或是其他类型的二进…

MATLAB用CNN-LSTM神经网络的语音情感分类深度学习研究

全文链接&#xff1a;https://tecdat.cn/?p38258 在语音处理领域&#xff0c;对语音情感的分类是一个重要的研究方向。本文将介绍如何通过结合二维卷积神经网络&#xff08;2 - D CNN&#xff09;和长短期记忆网络&#xff08;LSTM&#xff09;构建一个用于语音分类任务的网络…

Android笔记(三十七):封装一个RecyclerView Item曝光工具——用于埋点上报

背景 项目中首页列表页需要统计每个item的曝光情况&#xff0c;给产品运营提供数据报表分析用户行为&#xff0c;于是封装了一个通用的列表Item曝光工具&#xff0c;方便曝光埋点上报 源码分析 核心就是监听RecyclerView的滚动&#xff0c;在滚动状态为SCROLL_STATE_IDLE的时…

Minikube 上安装 Argo Workflow

文章目录 步骤 1&#xff1a;启动 Minikube 集群步骤 2&#xff1a;安装Argo Workflow步骤 3&#xff1a;访问UI创建流水线任务参考 前提条件&#xff1a; Minikube&#xff1a;确保你已经安装并启动了 Minikube。 kubectl&#xff1a;确保你已经安装并配置了 kubectl&#xff…

GCP Cloud Storage 的lock retention policy是什么

简介 Google Cloud Storage 的锁定保留策略&#xff08;Lock Retention Policy&#xff09;是一种用于保护存储桶中对象数据的功能。它允许用户设置一个保留期&#xff0c;在此期间对象不能被删除或覆盖。这对于确保数据的长期保留和合规性非常重要&#xff0c;尤其是在需要满…

STM32设计防丢防摔智能行李箱

目录 目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 1.电路图采用Altium Designer进行设计&#xff1a; 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 随着科技的不断发展&#xff0c;嵌入式系统、物联网技术、智能设备…

CSP/信奥赛C++语法基础刷题训练(11):洛谷P5743:猴子吃桃

CSP/信奥赛C语法基础刷题训练&#xff08;11&#xff09;&#xff1a;洛谷P5743&#xff1a;猴子吃桃 题目描述 一只小猴买了若干个桃子。第一天他刚好吃了这些桃子的一半&#xff0c;又贪嘴多吃了一个&#xff1b;接下来的每一天它都会吃剩余的桃子的一半外加一个。第 n n n…

控制器ThinkPHP6

五、控制器中对数组值的返回 在做接口服务时&#xff0c;很多时候回使用数组作为返回值&#xff0c;那么数组如何返回成 json呢&#xff1f; 在 tp6 中返回json 很简单&#xff0c;直接使用 json 进行返回即可&#xff0c;例如&#xff1a; public function index(){$resarra…

洛谷刷题日记||基础篇8

#include <iostream> #include <vector> using namespace std;int N, M; // N为行数&#xff0c;M为列数 vector<vector<char>> field; // 表示田地的网格&#xff0c;每个元素是W或. vector<vector<bool>> visited; // 用来记录网格是否访…

Qwen2.5-3B-Instruct-GGUF部署

注册账号&#xff1a; 魔搭社区 等一会&#xff1a; 部署好了&#xff1a; 立即使用&#xff1a; 您部署的服务提供OpenAI API接口&#xff0c;可通过OpenAI SDK进行调用。请确保您的服务处于正常运行状态&#xff0c;并预先安装OpenAI SDK: pip install openai 在本地新建…

恶意PDF文档分析记录

0x1 PDF是什么 PDF&#xff08;便携式文件格式&#xff0c;Portable Document Format&#xff09;是由Adobe Systems在1993年用於文件交换所发展出的文件格式。 因为PDF的文件格式性质广泛用于商业办公&#xff0c;引起众多攻击者对其开展技术研究&#xff0c;在一些APT&#…

Spring-事务学习

spring事务 1. 什么是事务? 事务其实是一个并发控制单位&#xff0c;是用户定义的一个操作序列&#xff0c;这些操作要么全部完成&#xff0c;要不全部不完成&#xff0c;是一个不可分割的工作单位。事务有 ACID 四个特性&#xff0c;即&#xff1a; 原子性&#xff08;Atom…