手机app开发之app inventor 开发实战(二),实现音乐软件的编程

时间:2024-05-18 19:25:13

由于瘟疫肆虐,我在家中宅了好几天,简直不能太无聊。一个偶然的机会,我在抖音上面发现了一个类似钢琴的APP,然后就试着用App inventor做一个可以实现音乐功能的软件,暂且可以把它叫做木琴。经过3次的修改,最终成了下面的样子。让我们来一起看看吧!

一、简单的作品描述

 

基本界面如图1所示

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

功能介绍:

 

  • 通过触摸屏幕上的彩色按钮播放八个不同的音符;
  • 按“播放”按钮,回放之前弹奏的音符;
  • 按“重置”按钮清除之前弹过的音符,以便输入新曲。

二、实现原理

  • 使用单一的声音组件来播放不同的音频文件;
  • 使用Clock组件来计算并实现两个音符之间的延时;
  • 在创建一个过程时做判断;
  • 创建能够自我调用的过程;

三、具体实现步骤

记得上次做的是蓝牙相关的实践,用的是汉语的界面,这次尝试用英文试一下!

首先创建前两个键,用按钮来实现:

1. 从面板(palette)的user interface组中拖出一个按钮。

  • BackgroundColor属性:为红色;

  • Text属性:为“C”;

  • Width属性:为“Fill parent”,使其占满屏幕;

  • Height属性:为40像素。

2. 重复上述步骤创建第二个按钮,名为Button2,放在Button1下面。Width及Height属性值同Button1,但BackgroundColor属性设为红色,Text属性设置为“D”。如下图:

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

3.其他键位的设计完全按照上面的步骤来实现,在此就不一一说明了。

添加其他组件

木琴不能没有声音,所以我创建一个Sound组件,名字为Sound1。MinimumInterval(最小间隔)属性设置为0(默认值为500毫秒)。这可以让我们的演奏要多快有多快,而不必等半秒钟(500毫秒)。

下载1.wav和2.wav,并加载到项目中,注意这里的声音文件必须保持原有文件名。

当某个按钮被点击时,用程序来实现播放声音的行为,即:如果Button1被点击,则播放1.wav,如果Button2被点击,播放2.wav。

若要切换到块编辑器,如下图所示,进行以下设置:

  1. 从Screen1项下的Button1抽屉里拖出Button1.Click块;
  2. 从Sound1抽屉里拖set Sound1.Source块,放置在Button1.Click块中;
  3. 输入“text”来创建一个文本块(而不是从Built-in项下的Text抽屉里拖出,这样更便捷。)设置文本值为“1.wav”,并与Sound1.Source块连接;
  4. 添加Sound1.Play块。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

 点击按钮时播放声音

对Button2进行同样设置,如图(只改了文件名),代码几乎完全重复。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

重复的代码提示我们最好是创建一个过程。用join块将数字(如1)与文本“.wav”连接起来,创造出正规的文件名(如“1.wav”)。下面是创建这个过程的步骤:

1. 在块编辑器中打开Procedures抽屉,拖出“to procedure”块;

2. 单击procedure将过程名改为playNote;

3. 点击procedure块左上角的蓝色方块呼出内部组件,将一个input x块插入“inputs”块;

4. 将input x块中的x改为number;

5. 将set Sound1.Source to块从Button1.Click事件处理程序中拖出,放在PlayNote过程内“do”的右边,Sound1.Play块也将随之移动;

6. 将1.wav块拖入垃圾桶;

7. 从Text抽屉中拖出join块放到set Sound1.Source to的插槽内;

8. 将鼠标悬停在playNote的number参数上,呼出并拖动get number块,并将其放入join块的第一个插槽中;

9. 从Text抽屉中拖出空文本块,放在join块的第二个插槽中;

注意:将文本值设置为“.wav”。(切记不要输入引号);

从Procedures抽屉中拖出call PlayNote块,放到空的Button1.Click内;

在number插槽中插入文本“1”。

现在,当Button1被点击时,过程PlayNote将以数字1为参数被调用。该过程将Sound1.Source属性设为“1.wav”,并播放该声音。

创建一个Button2.Click块,调用参数为2的PlayNote过程。(可以复制现有的PlayNote块,将其移动到Button2.Click块内,并将参数更改为2;也可以复制整个Button1.Click块,然后将Button1改为Button2,再将参数1改为2。)程序如图所示。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

图 创建一个过程来演奏音符

四、Android加载声音

此时在手机上测试程序并不能成功:第一次按键时,弹出错误提示:“Error 703:Unable to play 1.wav”(不能播放1.wav);第二次再按同一个键时,才听到声音。

原因:Android系统是在程序运行时才加载声音文件(只需加载一次),加载过程需要一点时间。第一次按键,当call Sound1 play块开始执行时,set Sound1.Source to块的加载任务尚未完成,因此系统给出错误提示;等到第二次按键时,声音文件已经加载完成,因此可以正常播放。

解决方法:直到程序启动之后,我们也没有对Sound1.Source进行设置,因此没有对声音做初始化。我们必须在程序启动时直接加载声音文件,如图所示。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

图 在应用启动时加载声音文件

五、实现其余的音符

两个按钮已经实现了演奏音符的功能,现在需要回到组件设计器,加载其余六个声音文件3.wav、4.wav、5.wav、6.wav、7.wav和8.wav,并添加其余六个音符。首先创建六个新Button组件,重复此前的步骤,

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

图 在组件设计器中放置其余的声音按钮

回到块编辑器中,为每个新按钮创建Click块并以相应的参数调用PlayNote过程。同样,在Screen.Initialize中加载新的声音文件,如图所示。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

 对按钮单击事件编程,使得键盘与音调相对应

六、记录并回放音符

为了实现回放功能,需要记录弹奏的音符并加以保存。除了要记录弹奏的音高(声音文件),还要记录两个音符之间的时间长度,否则将无法表现两个连续快弹音符与两个间隔10秒的音符之间的差别。

实现原理:维护两个列表,每弹奏一个音符,两个列表中都会各自添加一条记录:

  • notes:包含与演奏的音符相对应的声音文件名,按照演奏顺序排列;
  • times:记录音符演奏时的时间点。

添加必要组件

在设计器中添加一个Clock组件及“播放”和“重置”按钮,按钮放在HorizontalArrangement中:

1. 拖入一个Clock组件,它将出现在“不可见组件”区域,取消勾选TimerEnabled属性。

2. 从layout组中拖出一个HorizontalArrangement组件放在按钮下面,Width属性设为“Fill parent”;

3. 从User Interface组中拖动一个按钮,改名为PlayButton,Text属性设为“播放”;

4. 拖出另一个按钮并放在PlayButton右侧,改名为ResetButton,Text属性设为“重置”。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

图 记录并回放声音的组件被添加到设计器中

七、记录音符及时间

实现原理:维护两个列表:notes与times,每次用户按下一个按钮,就向列表中添加一项:

1. 从Variables抽屉中拖出一个initialize global name to块来定义一个新的变量;

2. 单击“name”将变量命名为“notes”;

3. 打开Lists抽屉,拖动一个make a list块,将其放置在变量notes的插槽中;

这样就定义了一个名为“notes”的空列表。重复上述步骤定义另一个变量,命名为“times”。块的样子如图所示。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

图 设置变量来记录音符

八、块的功能

每演奏一个音符,需要保存两项数据:声音文件名(保存到notes列表),以及演奏瞬间的时刻(保存到times列表)。用Clock1.Now块来记录时刻,它返回当前时刻的时间值,精确到毫秒。这些数据可以通过Sound1.Source和Clock1.Now块获得,将分别被添加到notes及times列表中。

 

 

添加一个Sound1.Vibrate块,通过振动来告知用户按键生效了。实现逻辑如下:

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

 为用户的“重置”操作提供反馈

九、音符的回放

实现原理:

  • 变量count用来跟踪notes列表中当前正在播放的音符的索引(位置);

  • 新过程 PlayBackNote,用来播放当前音符,并移动到下一个音符;

  • 编写PlayButton.Click事件处理程序,设置count为1,只要列表中有保存的音符,就调用PlayBackNote。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

图 回放被记录下来的音符

自我调用-----数学递归

1. 在第一次调用PlayBackNote时,count= 1:

  • Sound1.Source被设置为在notes中的第1项,即1.wav;

  • 调用Sound1.Play,播放1.wav;

  • 由于count值(1)小于notes的长度(3),因此count递增为2,并再次调用PlayBackNote;

2. 第二次调用PlayBackNote时,count=2:

  • Sound1.Source被设置为notes中的第2项,即3.wav;

  • 调用Sound1.Play,播放3.wav;

  • 由于count(2)小于notes的长度(3),因此count递增为3,并再次调用PlayBackNote;

3. 第三次调用PlayBackNote时,count=3:

  • Sound1.Source被设置为notes中的第3项,即6.wav;

  • 调用Sound1.Play,播放6.wav;

  • 由于count(3)不小于notes的长度(3),因此跳出if块,回放结束。
  • 递归是正确的,但需要在两次调用PlayBackNote之间添加延迟功能。

十、播放适当延迟的音符

实现原理:

延迟的设定与两个音符之间的时间差有关,用clock来为这个时间差计时。创建Clock1.Timer事件并编写事件处理程序,来说明计时结束时将发生的事情。

手机app开发之app inventor 开发实战(二),实现音乐软件的编程

图  在音符之间加入延迟

块的功能

现在假设两个列表中记录了以下内容:

  • notes:1.wav,3.wav,6.wav

  • times:12:00:00,12:00:01,12:00:04

如图所示,在PlayButton.Click中设置count为1,并调用PlayBackNote。

1. 第一次调用PlayBackNote时,count= 1:

  • Sound1.Source被设置为notes中的第1项,即“1.wav”;

  • 调用Sound1.Play播放1.wav;

  • 因为count(1)小于notes的长度(3),于是Clock1.TimerInterval被设置为times列表中的第1项(12:00:00)与第2项(12:00:01)之间的时间差:1秒。Count递增到2,启用Clock1.Timer并开始计时;

Clock1.Timer开始计时,间隔1秒之后,计时结束,定时器暂时禁用,并调用PlayBackNote。

2. 第二次调用PlayBackNote时,count= 2 :

  • Sound1.Source被设置为notes中的第2项,即“3.wav”;

  • 调用Sound1.Play播放3.wav;

  • 因为count(2)小于notes的长度(3),于是Clock1.TimerInterval被设置为times列表中的第2项(12:00:01)与第3项(12:00:04)之间的时间差:3秒。Count递增到3,启用Clock1.Timer并开始计时;

Clock1.Timer计时开始,间隔3秒之后,定时器暂时禁用,并调用PlayBackNote。

3. 第三次调用PlayBackNote时,count= 3 :

  • Sound1.Source被设置为notes中的第3项,即“6.wav”;

  • 调用Sound1.Play来播放6.wav;

  • 由于count(3)不小于notes的长度(3),跳出if块,回放完成。

最后通过扫描二维码下载制作好的软件,来打发无聊的时光吧!

本次实践的最大收获是:编写一个能自我调用的过程不仅是可能的,有时也是必要的。递归就可以实现。在编写递归过程时,一定要确保为程序的退出设定一个基本条件,否则程序将陷入无限循环。好了今天就分享的这里吧,有问题可以通过下方留言。

资源下载

1.wav

2.wav

3.wav

4.wav

5.wav

6.wav

7.wav

8.wav