拋磚引玉:純前端代碼讓手機變為一把吉他

来源:https://www.cnblogs.com/vvjiang/archive/2022/09/01/16645840.html
-Advertisement-
Play Games

前言 前段時間一時興起想學一下吉他,但是一門樂器要演奏成“能聽”的程度也不是一天兩天的事情,對我這種音樂基礎為 0 的人來說學習周期太長了,不想耗費太多時間在學習樂器上面,於是想找個取巧的方法。 最終方案就是做了個簡單粗陋的微信小程式 Demo 去彈奏吉他樂,勉強算是成功吧,可以很簡單地彈奏出樂曲。 ...


前言

前段時間一時興起想學一下吉他,但是一門樂器要演奏成“能聽”的程度也不是一天兩天的事情,對我這種音樂基礎為 0 的人來說學習周期太長了,不想耗費太多時間在學習樂器上面,於是想找個取巧的方法。

最終方案就是做了個簡單粗陋的微信小程式 Demo 去彈奏吉他樂,勉強算是成功吧,可以很簡單地彈奏出樂曲。

大概一小時的學習周期(含學習簡譜時間),可以彈出傳統樂器學習一兩個月的效果。

但是這個小程式有一個 BUG ,導致它也只能算是一個失敗品。

文章最後會附上開源代碼地址,且容我先拋出這塊磚,希望能引來玉吧。

使用

這裡貼出微信小程式的二維碼,小程式的名字叫冒牌吉他手,大家可以體驗一下。

主界面

本小程式有四個模式:自由模式、簡單模式、靈魂模式和自動模式。

自由模式下,您可以按 1,2,3,4,5,6,7 等七個音符按鍵,分別對應 do(哆)、re(來)、mi(咪)、fa(發)、sol(唆)、la(拉)、si(西)。同時可以切換高低音和節拍按鍵來分別控制音高和音長。

簡單模式下,程式會自動根據簡譜識別出音高和音長,您只需要按七個音符按鍵即可。預設設置的簡譜是《成都》。

靈魂模式下,程式會自動根據簡譜識別出音高、音長和音符,您只需要按一個音符按鍵即可。

自動模式下,程式會自動根據簡譜彈奏音樂,您無需操作。

編輯簡譜時,會自動定位到簡譜最末尾的位置,且只支持新增和刪除。
刪除:點擊刪除按鈕會直接刪除最末尾的簡譜符號。

新增:按下相應的音符鍵,會結合當前的高低音和節拍按鍵,新增對應的簡譜符號。

另外編輯模式下會多出來兩個新的按鍵,分別是音符按鍵最左側的“0”鍵和音符按鍵最右側的“|”鍵。

“0”鍵代表簡譜上的休止符。

“|”鍵表示表示簡譜的分隔符,只用來標識,不輸入也是可以的。

方案與原理

這個小程式完全是前端代碼實現,無需服務端,並且不需要音頻資源文件。

之所以做成這樣,是因為微信小程式上面現在放資源文件是要給騰訊打錢的,而單純的代碼是不需要的 o(∩_∩)o

技術上的實現說起來也很簡單,使用 AudioContext 來實現,音頻文件是預先將 mp3 進行 base64 編碼,使用時再轉換成AudioContext的 buffer 數組來實現。

在做這個之前我對AudioContext一無所知,網上這類材料也很少,算是非常冷門了,主要是參考了webaudiofont這個項目來實現。

但是這裡有個問題,微信小程式的AudioContext是自己實現的一套機制,與 web 標準的AudioContext不一樣,而且微信小程式這裡另外提供了一個WebAudioContext的,不過還是與 AudioContext 標準介面有些差異。

所以我這裡也做了一些相容上的處理。

順便說一下,在代碼中簡單修改一下音源文件 0255_GeneralUserGS_sf2_file.js 為其他音源文件,也可以彈奏其他的樂器,比如鋼琴嗩吶之類的。

關鍵代碼

以下貼出播放音樂的關鍵代碼:

import { _tone_0255_GeneralUserGS_sf2_file } from "../../utils/0255_GeneralUserGS_sf2_file";

let actx = wx.createWebAudioContext();
let player = new WebAudioFontPlayer();
player.adjustPreset(actx, _tone_0255_GeneralUserGS_sf2_file);

// 根據音高,音符,節拍來播放音樂
playMusic(type, char, pai, time = 0) {
    if (char === "0") {
    return;
    }
    const pitch = levels[type] * 12 + note[char - 1];
    player.queueWaveTable(
        actx,
        actx.destination,
        _tone_0255_GeneralUserGS_sf2_file,
        time,
        pitch,
        TIME_RATE * parseFloat(pai),
        1
    );
},

在引入吉他音頻源文件_tone_0255_GeneralUserGS_sf2_file後,我們先調用微信小程式的wx.createWebAudioContext函數,創建一個AudioContext實例,然後使用封裝好的WebAudioFontPlayer類構建一個播放器實例player,然後調用player.adjustPreset函數預先解析音頻源文件。

接著在具體播放音樂的函數中playMusic,我們使用player.queueWaveTable播放音樂。

傳入的實參分別表示:

* actx // AudioContext實例
* actx.destination //  AudioContext實例的渲染設備,一般就是揚聲器之類的
* _tone_0255_GeneralUserGS_sf2_file // 音頻源文件
* time // 音頻響起的起始時間
* pitch // 音高
* TIME_RATE * parseFloat(pai) // 這裡是根據傳入的節拍計算出這個音調播放的持續時間
* 1 // 這裡表示的是音量

另外註意一點:

const pitch = levels[type] * 12 + note[char - 1];

這行代碼是我參考相關文件得出的計算音高的計算公式,保持固定就好了,其中涉及的魔法數字也是沒辦法的事情。

除了以上的關鍵代碼,其他的代碼比如輸入簡譜,自動定位當前的簡譜,以及播放音樂的控制邏輯大多也不算難,所以就不單獨列出了。

缺陷與失敗的修複方案

這個小程式在自由模式、簡單模式和靈魂模式下運行還算較好。

但是在真實手機上,自動模式是有問題的(在 PC 的開發者工具上自動模式沒有問題)。

這個問題就是自動模式下,播放的聲音有雜音。

自動模式嘗試過兩個方案:
一個方案使用的是 setTimeout,播放一個音調後再播放下一個音調,在 PC 上表現良好,在手機上會有延遲,雜音問題很小。
一個方案使用的是 queueWaveTable 自己的時間機制去播放,沒有延遲,但是雜音問題嚴重。

由於這兩套方案在開發者工具上都表現良好,只在真機上有問題,猜測只能是移動端性能不佳,或者是微信小程式的WebAudioContext實現有問題。

我實際發佈的小程式採用的是第一種方案,不過第二種我也在代碼中實現了,在代碼中已註釋。

其實我更傾向於第二種方案,因為 setTimeout 在真機上的延遲聽起來像亂彈的,只是第二種的雜音問題實在太嚴重才不得不選第一種。

總的來說,在真機上,自動模式都不咋樣。

總結

因為對這個方面實在沒什麼深入研究的欲望,隨著興趣減淡,也就偃旗息鼓了。

雖然自動模式確實存在問題,但是靈魂模式也不錯了,也更好玩一點。

也算是做了一些東西的,如果有後來者可以解決這個問題,或者有感興趣的可以在這個基礎上再修複或者拓展吧。

這裡給出開源的倉庫地址:fake-guitarist

作者:韓子盧
出處:https://www.cnblogs.com/vvjiang/
本博客文章均為作者原創,轉載請註明作者和原文鏈接。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 年少不知Vue的好,錯把layui當成寶 不用太在意這句話,只是我的感慨 1.安裝與配置: 在HBuilderX創建Uniapp項目,可以不用啟動uniCloud,Vue的版本隨意選擇即可,常用vue2.x。 根據官方文檔的提示,安裝uview。 點擊紅框中的按鈕打開終端。 執行如下的命令: npm ...
  • 好家伙,本篇介紹敵機 好了,按照慣例我們來理一下思路: 我們有一個敵機類,第一步當然是實例一個敵機對象, 然後我們把這個敵機放入我們的敵機群(敵機數組) 然後是熟悉的移動和繪製 那我們回顧一下子彈的生成邏輯 變數: 子彈 bullet 彈夾(用來裝子彈的東西)bulletList[] 方法:裝填子彈 ...
  • 題目來源於前端面試寶典——選擇題欄 習題 class Counter { // 二、count 屬性被包含在類 Counter 的構造函數與 increment 方法。 constructor() { this.count = 0 } increment() { this.count++ } } / ...
  • 代理模式 1 定義 為其他對象提供一種代理以控制對這個對象的訪問 在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。 2 應用舉例 2.1 緩存代理 現在我們有一個可以查詢城市經緯度的函數: const getLatLng = (addres ...
  • 1 含義 頁面導航就是指頁面之間的一個跳轉 1.1 導航的實現方式 Web網頁端 微信小程式 1.2 聲明式導航 1.2.1 導航到tabBar頁面 使用<navigator>組件跳轉到指定的tabBar頁面 url:以/開頭,標識要跳轉的頁面地址 open-type:必須為switchTab,表示 ...
  • 作為一名H5開發人員可以使用hbuilder或apicloud。但是,到打包和發佈時,被申請ios證書和上架ipa文件給了困難。由於官方提供的方法,申請證書需要使用Mac電腦然後使用Mac電腦中的密鑰鏈訪問,去申請證書csr文件。然後再去蘋果開發者中心申請。 但是,Mac或未使用的Mac朋友都被 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 小伙伴們,在開發中有沒有遇到過發佈帖子或者實時聊天需要發送到一些emoji表情的。 但是每當我們直接將emoji表情提交到後臺的介面又會報錯,因為字元串的格式不一致導致數據無法添加致資料庫。那麼作為前端的我們就要將我們要提交的數據,通過如 ...
  • 自定義過渡的類名 點擊打開視頻講解更加詳細 我們可以通過以下 attribute 來自定義過渡類名: enter-class enter-active-class enter-to-class (2.1.8+) leave-class leave-active-class leave-to-clas ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...