歌詞相關實現

歌詞相關

  1. 歌詞數據模型
// Lyric.swift
class Lyric: BaseModel {/// 是否是精確到字的歌詞var isAccurate:Bool = false/// 所有的歌詞var datum:Array<LyricLine>!
}// LyricLine.swift
class LyricLine: BaseModel {/// 整行歌詞var data:String!/// 開始時間(毫秒)var startTime:Int!/// 每個字(KSC格式)var words:Array<String>!/// 每個字的持續時間(KSC格式)var wordDurations:Array<Int>!/// 結束時間var endTime:Int = 0
}
  1. 歌詞解析
// LRCLyricParser.swift - LRC格式解析
static func parse(_ data:String) -> Lyric {let result = Lyric()result.isAccurate = false  // LRC格式不精確到字// 按行分割let strings = data.components(separatedBy: "\n")for line in strings {if line.starts(with: "[0") {// 解析時間戳和歌詞內容// 例如:[00:00.300]愛的代價let lyricLine = LyricLine()// 解析時間戳lyricLine.startTime = DateUtil.parseToInt(commands[0])// 解析歌詞內容lyricLine.data = commands[1]result.datum.append(lyricLine)}}return result
}// KSCLyricParser.swift - KSC格式解析
static func parse(_ data:String) -> Lyric {let result = Lyric()result.isAccurate = true  // KSC格式精確到字// 解析每行歌詞// 例如:karaoke.add('00:27.487', '00:32.068', '一時失志不免怨嘆', '347,373,1077,320,344,386,638,1096')// 包含每個字的持續時間
}
  1. 歌詞顯示視圖
// LyricListView.swift
class LyricListView: BaseRelativeLayout {var data: Lyric?var tableView: UITableView!var datum: [Any] = []/// 當前顯示的歌詞行號var lyricLineNumber: Int = 0/// 歌詞上下填充的占位行數var lyricPlaceholderSize = 0func setProgress(_ progress: Float) {// 1. 計算當前應該顯示哪一行let newLineNumber = LyricUtil.getLineNumber(data!, progress) + lyricPlaceholderSize// 2. 如果行號變化,滾動到新位置if newLineNumber != lyricLineNumber {scrollPosition(newLineNumber)lyricLineNumber = newLineNumber}// 3. 如果是精確到字的歌詞,更新當前字的位置if data!.isAccurate {if let object = datum[lyricLineNumber] as? LyricLine {// 計算當前是第幾個字let lyricCurrentWordIndex = LyricUtil.getWordIndex(object, progress)// 計算當前字已經播放的時間let wordPlayedTime = LyricUtil.getWordPlayedTime(object, progress)// 更新顯示if let cell = getCell(lyricLineNumber) {cell.lineView.lyricCurrentWordIndex = lyricCurrentWordIndexcell.lineView.wordPlayedTime = wordPlayedTimecell.lineView.setNeedsDisplay()}}}}
}
  1. 歌詞行視圖
// LyricLineView.swift
class LyricLineView: UIView {var data: LyricLine?var accurate: Bool = falsevar lineSelected = falseoverride func draw(_ rect: CGRect) {if let data = self.data {if accurate {// 精確到字的歌詞繪制// 1. 繪制整行歌詞(灰色)wordStringNSString.draw(at: point, withAttributes: attributes)if lineSelected {// 2. 計算高亮部分的寬度let lineLyricPlayedWidth = calculatePlayedWidth()// 3. 繪制高亮部分(紅色)let selectedRect = CGRect(x: point.x, y: point.y, width: lineLyricPlayedWidth, height: size.height)context.clip(to: selectedRect)attributes[.foregroundColor] = lyricSelectedTextColorwordStringNSString.draw(at: point, withAttributes: attributes)}} else {// 普通歌詞繪制if lineSelected {attributes[.foregroundColor] = lyricSelectedTextColor}wordStringNSString.draw(at: point, withAttributes: attributes)}}}
}
  1. 時間計算工具
// LyricUtil.swift
class LyricUtil {/// 計算當前時間對應的歌詞行static func getLineNumber(_ lyric: Lyric, _ progress: Float) -> Int {let progress = progress * 1000  // 轉為毫秒// 倒序遍歷找到第一個開始時間小于等于當前時間的行for (index, value) in lyric.datum.enumerated().reversed() {if progress >= Float(value.startTime) {return index}}return 0}/// 計算當前時間對應的字(KSC格式)static func getWordIndex(_ line: LyricLine, _ progress: Float) -> Int {let newTime = Int(progress * 1000)var startTime = line.startTime!// 累加每個字的持續時間,找到當前字for (index, value) in line.wordDurations!.enumerated() {startTime = startTime + valueif newTime < startTime {return index}}return -1}
}
  1. 播放器集成
// MusicPlayerManager.swift
class MusicPlayerManager {func prepareLyric() {// 1. 檢查是否有歌詞if data!.parsedLyric != nil {onLyricReady()} else if SuperStringUtil.isNotBlank(data!.lyric) {// 2. 解析本地歌詞parseLyric()} else {// 3. 從網絡獲取歌詞let urlString = data?.lrcif let url = URL(string: urlString ?? "") {// 下載并解析歌詞}}}// 播放進度更新時調用func updateProgress(_ progress: Float) {// 更新歌詞顯示lyricView?.setProgress(progress)}
}

這個實現的主要特點:

  1. 支持多種格式

    • LRC:簡單的時間戳+歌詞格式
    • KSC:支持精確到字的歌詞顯示
  2. 精確的時間控制

    • 毫秒級的時間計算
    • 支持精確到字的歌詞顯示
    • 平滑的滾動效果
  3. 良好的用戶體驗

    • 歌詞居中顯示
    • 支持拖拽交互
    • 顯示拖拽位置的時間
    • 點擊可以跳轉到對應位置
  4. 性能優化

    • 使用占位行實現居中效果
    • 按需更新顯示
    • 避免不必要的重繪

歌詞同步機制:

  1. 時間同步機制
// LyricListView.swift
func setProgress(_ progress: Float) {if datum.count > 0 {// 1. 根據當前播放時間,計算應該顯示哪一行歌詞let newLineNumber = LyricUtil.getLineNumber(data!, progress) + lyricPlaceholderSize//所以為什么不二分// 2. 如果行號發生變化,滾動到新位置if newLineNumber != lyricLineNumber {scrollPosition(newLineNumber)lyricLineNumber = newLineNumber}}
}
  1. 時間計算
// LyricUtil.swift
static func getLineNumber(_ lyric: Lyric, _ progress: Float) -> Int {// 將播放時間轉換為毫秒let progress = progress * 1000// 倒序遍歷歌詞行,找到第一個開始時間小于等于當前時間的行for (index, value) in lyric.datum.enumerated().reversed() {if progress >= Float(value.startTime) {return index}}return 0
}
  1. 滾動實現
// LyricListView.swift
func scrollPosition(_ lineNumber: Int) {let indexPaht = IndexPath(item: lineNumber, section: 0)if tableView.visibleCells.count > 0 {// 使用動畫滾動到當前行,并保持居中tableView.selectRow(at: indexPaht, animated: true, scrollPosition: .middle)}
}
  1. 播放器集成
// MusicPlayerManager.swift
class MusicPlayerManager {// 播放進度更新時調用func updateProgress(_ progress: Float) {// 更新歌詞顯示lyricView?.setProgress(progress)}
}

同步流程:

  1. 準備階段

    • 解析歌詞文件,獲取每行歌詞的開始時間
    • 將歌詞數據存儲在 parsedLyric
  2. 播放階段

    • 播放器實時提供播放進度(秒)
    • 調用 setProgress 方法更新歌詞顯示
  3. 同步計算

    • 將播放時間轉換為毫秒
    • 遍歷歌詞行,找到當前時間對應的行
    • 如果行號變化,滾動到新位置
  4. 顯示更新

    • 使用動畫滾動到當前歌詞行
    • 保持當前行在屏幕中央
    • 高亮顯示當前行

關鍵點:

  1. 使用毫秒級的時間計算,保證同步精度
  2. 倒序遍歷歌詞行,提高查找效率
  3. 使用動畫滾動,提供流暢的視覺效果
  4. 保持當前行居中顯示,提升用戶體驗

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/71988.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71988.shtml
英文地址,請注明出處:http://en.pswp.cn/web/71988.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

紡織服裝制造行業現狀 內檢實驗室系統在紡織服裝制造行業的應用

在紡織服裝制造行業&#xff0c;內檢實驗室LIMS系統&#xff08;實驗室信息管理系統&#xff09;已成為提升檢測效率、優化質量控制和滿足行業合規性要求的關鍵工具。隨著行業競爭的加劇和消費者對產品質量要求的提高&#xff0c;紡織服裝制造企業需要更加高效、準確的檢測流程…

K8s 1.27.1 實戰系列(十一)ConfigMap

ConfigMap 是 Kubernetes 中管理非敏感配置的核心資源,通過解耦應用與配置實現靈活性和可維護性。 一、ConfigMap 的核心功能及優勢 ?1、配置解耦 將配置文件(如數據庫地址、日志級別)與容器鏡像分離,支持動態更新而無需重建鏡像。 ?2、多形式注入 ?環境變量:將鍵值…

3分鐘復現 Manus 超強開源項目 OpenManus

文章目錄 前言什么是 OpenManus構建方式環境準備克隆代碼倉庫安裝依賴配置 LLM API運行 OpenManus 效果演示總結個人簡介 前言 近期人工智能領域迎來了一位備受矚目的新星——Manus。Manus 能夠獨立執行復雜的現實任務&#xff0c;無需人工干預。由于限制原因大部分人無法體驗…

從零開始學機器學習——構建一個推薦web應用

首先給大家介紹一個很好用的學習地址:https://cloudstudio.net/columns 今天,我們終于將分類器這一章節學習完活了,和回歸一樣,最后一章節用來構建web應用程序,我們會回顧之前所學的知識點,并新增一個web應用用來讓模型和用戶交互。所以今天的主題是美食推薦。 美食推薦…

【最后203篇系列】014 AI機器人-1

說明 終于開張了&#xff0c;我覺得AI機器人是一件真正正確&#xff0c;具有商業價值的事。 把AI機器人當成一筆生意&#xff0c;我如何做好這筆生意&#xff1f;一端是業務價值&#xff0c;另一端是技術支撐。如何構造高質量的內容和服務&#xff0c;如何確保技術的廣度和深度…

【大模型統一集成項目】如何封裝多個大模型 API 調用

&#x1f31f; 在這系列文章中&#xff0c;我們將一起探索如何搭建一個支持大模型集成項目 NexLM 的開發過程&#xff0c;從 架構設計 到 代碼實戰&#xff0c;逐步搭建一個支持 多種大模型&#xff08;GPT-4、DeepSeek 等&#xff09; 的 一站式大模型集成與管理平臺&#xff…

AI4CODE】3 Trae 錘一個貪吃蛇的小游戲

【AI4CODE】目錄 【AI4CODE】1 Trae CN 錐安裝配置與遷移 【AI4CODE】2 Trae 錘一個 To-Do-List 這次還是采用 HTML/CSS/JAVASCRIPT 技術棧 Trae 錘一個貪吃蛇的小游戲。 1 環境準備 創建一個 Snake 的子文件夾&#xff0c;清除以前的會話記錄。 2 開始構建 2.1 輸入會…

【簡答題002】Java變量簡答題

博主會經常補充完善這里面問題的答案。希望可以得到大家的一鍵三連支持&#xff0c;你的鼓勵是我堅持下去的最大動力&#xff01;謝謝&#xff01; 001 什么是Java變量&#xff1f; Java變量是用來存儲數據并在程序中引用的命名空間。 002 Java變量有哪些類型&#xff1f; J…

從零開發Chrome廣告攔截插件:開發、打包到發布全攻略

從零開發Chrome廣告攔截插件&#xff1a;開發、打包到發布全攻略 想打造一個屬于自己的Chrome插件&#xff0c;既能攔截煩人的廣告&#xff0c;又能優雅地發布到Chrome Web Store&#xff1f;別擔心&#xff0c;這篇教程將帶你從零開始&#xff0c;動手開發一個功能強大且美觀…

基于騰訊云高性能HAI-CPU的跨境電商客服助手全鏈路解析

跨境電商的背景以及痛點 根據Statista數據&#xff0c;2025年全球跨境電商市場規模預計達6.57萬億美元&#xff0c;年增長率保持在12.5% 。隨著平臺規則趨嚴&#xff08;如亞馬遜封店潮&#xff09;&#xff0c;更多賣家選擇自建獨立站&#xff0c;2024年獨立站占比已達35%。A…

maven的項目構建

常用構建命令 命令說明mvn clean清理編譯結果&#xff08;刪掉target目錄&#xff09;mvn compile編譯核心代碼&#xff0c;生成target目錄mvn test-compile編譯測試代碼&#xff0c;生成target目錄mvn test執行測試方法mvn package打包&#xff0c;生成jar或war文件mvn insta…

定時任務和分布式任務框架

文章目錄 一 Spring Task1.@Scheduled注解介紹2 基本用法(1)使用@EnableScheduling修飾啟動類(2)創建定時任務的類(3)fixedDelay(4)fixedRate(5)cron3 執行多個任務4 設置異步執行5 @Async使用自定義線程池6 缺點二 xxl-job介紹架構圖與其他任務調度平臺的比較運行調…

git安裝,配置SSH公鑰(查看版本、安裝路徑,更新版本)git常用指令

目錄 一、git下載安裝 1、下載git 2、安裝Git?&#xff1a; 二、配置SSH公鑰 三、查看安裝路徑、查看版本、更新版本 四、git常用指令 1、倉庫初始化與管理 2、配置 3、工作區與暫存區管理 4、提交 5、分支管理 6、遠程倉庫管理 7、版本控制 8、其他高級操作 一…

[Web]ServletContext域(Application)

簡介 Web應用的Application域的實現是通過ServletContext對象實現的。整個Web應用程序的所有資源共享這個域。生命周期與Web應用程序相同&#xff0c;即當前Web應用程序啟動時&#xff08;以服務器視角而非訪客視角&#xff09;出生&#xff0c;Web應用服務程序關閉時停止。 通…

qt c++ 進程和線程

在Qt C開發中&#xff0c;進程&#xff08;Process&#xff09;和線程&#xff08;Thread&#xff09;是兩種不同的并發模型&#xff0c;各有適用場景和實現方式。以下是詳細對比和實際開發中的用法總結&#xff1a; 一、進程&#xff08;Process&#xff09; 進程是操作系統資…

【鴻蒙開發】OpenHarmony調測工具hdc使用教程(設備開發者)

00. 目錄 文章目錄 00. 目錄01. OpenHarmony概述02. hdc簡介03. hdc獲取04. option相關的命令05. 查詢設備列表的命令06. 服務進程相關命令07. 網絡相關的命令08. 文件相關的命令09. 應用相關的命令10. 調試相關的命令11. 常見問題12. 附錄 01. OpenHarmony概述 OpenHarmony是…

手寫簡易Tomcat核心實現:深入理解Servlet容器原理

目錄 一、Tomcat概況 1. tomcat全局圖 2.項目結構概覽 二、實現步驟詳解 2.1 基礎工具包&#xff08;com.qcby.util&#xff09; 2.1.1 ResponseUtil&#xff1a;HTTP響應生成工具 2.1.2 SearchClassUtil&#xff1a;類掃描工具 2.1.3 WebServlet&#xff1a;自定義注解…

【Java開發指南 | 第三十四篇】IDEA沒有Java Enterprise——解決方法

讀者可訂閱專欄&#xff1a;Java開發指南 |【CSDN秋說】 文章目錄 1、新建Java項目2、單擊項目名&#xff0c;并連續按兩次shift鍵3、在搜索欄搜索"添加框架支持"4、勾選Web應用程序5、最終界面6、添加Tomcat 1、新建Java項目 2、單擊項目名&#xff0c;并連續按兩次…

在MATLAB中實現PID控制仿真

在MATLAB中實現PID控制仿真可以通過代碼編程或Simulink圖形化建模兩種方式完成。以下是兩種方法的詳細操作步驟和示例&#xff1a; 方法1&#xff1a;使用MATLAB腳本編程&#xff08;基于控制系統工具箱&#xff09; 步驟1&#xff1a;定義被控對象的數學模型 假設被控對象是…

Conda常用命令匯總

Conda 是一個流行的包管理器和環境管理工具&#xff0c;廣泛應用于數據科學、機器學習等領域。它可以幫助我們管理 Python 包以及不同版本的環境&#xff0c;避免包沖突&#xff0c;提升項目的可復現性。以下是一些常用的 Conda 命令&#xff0c;涵蓋環境創建、管理、包安裝等常…