沃尔夫拉姆 计算符合知识

我如何用Wolfram语言和Unity游戏引擎构建虚拟钢琴

我如何用Wolfram语言和Unity游戏引擎构建虚拟钢琴

你知道什么比学钢琴更难吗?学习钢琴没有钢琴,也没有任何音乐理论知识。对我来说,买一架真正的钢琴是不可能的;我在大学的小公寓里既没有资金,也没有空间。所以很自然,看起来我必须自己构建一个——当然是数字的。幸运的是,我数学软件,团结还有几个小时的空闲时间。因为在Unity工作非常快速高效Wolfram语言UnityLink公司,我创建了一个可播放的钢琴部分,甚至在这个过程中学习了一些音乐理论。

首先,我确定建造钢琴需要以下几点:

  • 每个音符的音频
  • 钢琴键的几何形状
  • 便携式、交互式、实时渲染音频和3D-physics引擎

前两项可以用Wolfram语言轻松完成。至于最后一个,我选择使用新推出的UnityLink-Wolfram语言和实时开发平台Unity之间的强大链接。使用UnityLink,现在可以将Wolfram Language在渲染、音频和物理方面令人印象深刻的模拟的优势与Unity将所有这三者高效打包为独立的应用程序,用于web、桌面、移动和控制台平台。

什么是钢琴?

在我深入研究代码之前,让我们研究一下钢琴的一些背景以及它所演奏的音符。了解物理钢琴背后的理论将有助于我们在Unity中更好地再现它。

钢琴起源于18世纪早期的意大利,由巴托洛梅奥·克里斯托福里从那时起,它经历了许多设计更改,最终形成了(大部分)标准化的密钥配置。

现代钢琴共有88个键,其中52个是白色的,用于演奏自然音符(A、B、C、D、E、F和G)。其余36个键为黑色,用于播放意外事故(A♯/B♭、C \9839»/D \9837;、D \9839;/E \98370;、F \9839«/G \9837.和G \9839ЎўA \9837')。符号代表锋利的平的分别是。在这里,您可以看到所有88个键及其相应的注释标记:

全钢琴键盘

注释可以进一步分为八度音阶,每个包含12个键。具有相同音符但八度不同的两个键将具有不同的音高。钢琴的八度音阶在这个图中用彩色编码:

倍频程

一架钢琴包含七个完整的八度音阶,琴尾有四个额外的琴键。这些额外的键允许在所有七个八度音阶中演奏A小调和C大调的音阶。

在这篇博客文章中,为了简单起见,我将重点介绍一个音阶(按顺序排列的音符列表),但可以应用此方法创建整个钢琴。让我们使用一种最常见的标尺——C大比例尺。这个音阶只包含按C、D、E、F、G、A和B顺序排列的自然音符。任何C音符都可以选择作为音阶的开始。在这里,我将使用第四个八度音阶中的C音(也称为C4或中间C):

第四个八度音阶

如果你仔细看看,你会发现我们钢琴的这一小节包含了所有七个自然音符和所有五个意外音符。请注意,我还在音阶中加入了下一个八度音阶(C5)的C键,因为这有助于“舍入”音阶:

中部C

制作音乐

呼!有了背景知识,我终于可以了解代码了。为了获得钢琴键的声音,我使用了这个符号音符,可以从大量乐器中生成任何音符。对于单个音符,只需给它音符名称、持续时间和乐器。包裹时音频,它创建了一个可以直接在笔记本中播放的音频对象:

下载Wolfram笔记本,了解本文中的代码
音频
&#10005

音频[音符[“C”,3,“钢琴”]]

要获得特定八度的音符,只需将八度数字连接到音符名称的末尾。例如,我可以使用下面显示的代码获得第四个八度音阶中的所有自然音符:

自然音符
&#10005

自然注释={“C”、“D”、“E”、“F”、“G”、“A”、“B”};表[Audio[SoundNote[note<>“4”]],{note,naturalNotes}//AudioJoin

生成几何图形

钢琴键的确切形状和尺寸因制造商而异。我选择通过将每个关键点近似为棱镜来保持简单。使用棱镜的优点是,我只需要指定基本多边形并向上挤出。但是,确保关键帧不重叠需要五种基本多边形变化:

五种基本多边形变化

剩下的就是将基本多边形转换为三维棱镜。这可以很容易地使用区域产品要将多边形乘以具有给定高度的线段,请执行以下操作:

line=边界网格区域
&#10005

线=边界网格区域[{{0},{height}},点[{{1},}]];区域=表[区域边界[RegionProduct[BoundaryMeshRegion[polygon],line]],{polygon,多边形}];行[区域]

准备项目

现在我有了音频和几何,是时候在Unity中结合它们来制作一架工作钢琴了。正如我之前提到的,这是通过UnityLink公司.

安装Unity后,加载UnityLink就像一个函数调用一样简单:

需要[“UnityLink`”]
&#10005

需要[“UnityLink`”]

我首先打开一个新的Unity项目,我将其命名为"我的钢琴":

UnityOpen(单元打开)
&#10005

UnityOpen[“我的钢琴”]

我的钢琴

随着项目的打开,我现在可以从Unity发送和接收数据。我最终想在一个场景-可以作为菜单、游戏级别或Unity应用程序的任何其他独特部分的3D环境。但在我创建场景,我必须首先将之前创建的音频和几何内容传输到Unity。添加后,我可以在我的场景.

虽然不是必需的,但最好使用项目中的子目录来组织Unity项目资产目录。这个资产目录包含项目中使用的所有资源(纹理、音频剪辑、网格等)。在这里显示的行中,我在资产目录使用创建单位资产目录:

创建单位资产目录
&#10005

CreateUnityAssetDirectory[{“网格”,“音频”,“材质”,“场景”,“脚本”}];

资产目录

现在我开始传输音频。我通过传递音频函数的每个注释CreateUnityAudioClip自动将其转换为Unity的音频剪辑对象并将其存储在资产目录。这些音频剪辑对象表示为UnityAudioClipWolfram语言中的表达式:

笔记
&#10005

注释={“C4”,“D4”,“E4”,”F4“,”G4“,‘A4’,‘B4’,‘C#4’,“D#4”,“F#4”、“G#4”,“A#4”和“C5”};clips=关联[表[audio=音频[音符[音符,2.5,“钢琴”]];注释->CreateUnityAudioClip[文件[“音频/note_”<>注释],音频],{注释,注释}]];clips//短片

音频

接下来,我转移我的钢琴键的几何形状。然而,这次我使用创建单位网格自动转换我的网格区域到Unity的网格对象,表示为UnityMesh公司Wolfram语言中的表达式。

meshs=表格
&#10005

meshs=表格[CreateUnityMesh[File[“Meshes/mesh_”<>ToString[i]],区域[[i]]],{i,长度[区域]}];网格//短

统一网格

我做了一些类似的事情来创建黑白材质,以及一个用于控制用户与钢琴键交互的脚本组件。为了简洁起见,我省略了这些内容,但完整的代码可以在本文的可下载笔记本中找到。

设置场景

拥有所有资产转移,我终于可以场景为我的钢琴。我首先创建一个新的默认值场景:

创建单元场景
&#10005

CreateUnityScene[文件[“场景/钢琴”]]

如果你是Unity的新手,这里有一个简短的描述场景.场景包含游戏对象,它又充当容器组件。您可以考虑场景作为一种环境,游戏对象作为环境中的事物组件作为这些事物的行为。

在我的钢琴里场景,我要做一个游戏对象对于每个键。然后,我将把前面创建的脚本组件附加到每个游戏对象上,这样当用户与它们交互时,它们就会发出声音并移动。

我可以一次只添加一个键;然而,这将被证明是乏味的,并且在未来很难扩展。相反,我在两个列表中定义了关于每个白键和每个黑键的信息。然后,我可以迭代这些列表,自动创建每个键。对于每个键,我指定它对应的计算机键盘键、它应该播放的音符以及它应该使用的网格索引。请注意,黑键的网格索引隐式假定为5:

白色键
&#10005

whiteKeys={<|“Keycode”->“q”,“Note”->“C4”,“Mesh”->3|>,顺序[关联[“Keycode”->“w”,“Note”->“D4”,“Mesh”->4],关联[“Keycode”->“e”,“Note”->“E4”,“Mesh”->2],关联[“Keycode”->“r”,“Note”->“F4”,“Mesh”->3],关联[“Keycode”->“t”,“Note”->“G4”,“Mesh”->4],关联[“Keycode”->“y”,“Note”->“A4”,“Mesh”->4],关联[“Keycode”->“u”,“Note”->“B4”,“Mesh”->2],关联[“Keycode”->“i”,“Note”->“C5”,“Mesh”->1]]};blackKeys={<|“Keycode”->“2”,“Note”->“C#4”|>,序列[关联[“Keycode”->“3”,“Note”->“D#4”],空,关联[“Keycode”->“5”,“Note”->“F#4”],协会[“Keycode”->“6”,“Note”->“G#4”],关联[“Keycode”->“7”,“Note”->“A#4”]]};

为了保持我的场景经过组织,我还将我的所有密钥分组到名为的父游戏对象下“钢琴音阶”:

parent=CreateUnityTransform
&#10005

parent=CreateUnityTransform[“钢琴音阶”]

我首先迭代所有的白色键:

做
&#10005

做[key=白色键[[i]];name=“Key”<>Key[“Note”]<>“(白色)”;go=CreateUnityGameObject[名称,网格[[key[“网格”]]];go[[“Transform”,“Position”]]={(i-1)*(whiteWidth+gap),0,0};go[[“Transform”,“Parent”]]=父级;script=CreateUnityComponent[go,“PianoKey”];script[[“Key”]]=Key[“Keycode”];script[[“Clip”]]=clips[key[“Note”]];,{i,长度[whiteKeys]}]

白色按键

后面是黑色键:

做
&#10005

做[key=whiteKeys〔〔i〕〕;name=“Key”<>Key[“Note”]<>“(白色)”;go=UnityLink`CreateUnityGameObject[名称,网格[[key[“网格”]]];go[[“Transform”,“Position”]]={(i-1)(whiteWidth+gap),0,0};go[[“Transform”,“Parent”]]=父级;script=UnityLink`CreateUnityComponent[go,“PianoKey”];script[[“Key”]]=Key[“Keycode”];script[[“Clip”]]=clips[key[“Note”]];,{i,长度〔blackKeys〕}]

黑色钥匙

对于每个键,我创建一个游戏对象与适当的网格使用CreateUnityGame对象.设置此位置后游戏对象,我通过传递游戏对象和脚本名称到CreateUnityComponent(创建单元组件).I通过指定该键的键码和音频剪辑完成。

就这样,我有一架正在工作的(部分)钢琴。然而,它看起来并没有尽可能好。为了解决这个问题,我调整了对象材质以及照明和相机(可下载笔记本中的完整代码)。这样,我们得到了最终结果:

最后一架钢琴

现在看起来好多了!在继续之前,我还想保存刚刚对我的场景通过呼叫保存UnityScene:

保存UnityScene
&#10005

SaveUnityScene[]

弹奏钢琴

要在Unity编辑器中测试钢琴,我可以使用UnityPlay游戏Unity停止播放编辑模式。当我对结果满意时,我可以使用UnityBuild公司.

以下命令将自动将项目构建为我当前平台(macOS)的项目目录中的文件:

UnityBuild公司
&#10005

UnityBuild[]

构建成功后,我可以立即打开并播放我的钢琴应用程序:

系统打开
&#10005

系统打开[%[“应用程序”]]

系统打开

在Unity中工作的优点之一是它能够构建许多平台,而无需更改代码。如果你能在一个平台上玩游戏,那么Unity很有可能会在这个平台上进行构建。

它甚至可以构建为在web浏览器中运行。去试试吧!

轮到你了!

这一小部分钢琴可以很容易地扩展到一个完整的钢琴键盘。有160多种风格和打击乐器可供选择音符,您还可以构建其他乐器,甚至可以将它们组合成单个合成器.

收件人开始工作使用Wolfram语言中的UnityLink,请访问联机文档页面或者试试示例项目。你可以用它做很多事情内置接口,我期待着看到您提出的项目Wolfram社区!

版本12为Wolfram语言引入了许多主要的新领域,包括与Unity游戏引擎的无缝接口。立即开始使用Wolfram|One或Mathematica在桌面或Wolfram Cloud中进行编码。

开始吧!

评论

加入讨论

!请输入您的评论(至少5个字符)。

!请输入您的姓名。

!请输入有效的电子邮件地址。

3条评论

  1. 这太不可思议了!作为一名音乐家和Mathematica的长期用户,我被软件的多功能性和背后开发人员的创造力所折服。继续努力。

    答复
  2. 亚历克,这太棒了!感谢您分享您的工作,并清楚地描述您的开发过程。绝对值得思考。

    答复
  3. 这太棒了。我想知道我是否可以用免费的Wolfram引擎尝试UnityLink。

    答复