基于matlab的数字电子琴的完全指导手册

发布时间 : 星期三 文章基于matlab的数字电子琴的完全指导手册更新完毕开始阅读

出设备资源;(3)为buf分配动态内存空间;(4)执行waveOutPrepareHeader函数为第2步获得的资源配缓冲区。具体操作很复杂,CSound类将其封装为一个初始化函数PrepareDevice。

对buf的操作,CSound类将其封装为FillBuf函数,功能是将指定波形、频率、幅值和相位的数字信号写入buf中。以下是该函数的源码。可以看出,该函数主要由三部分组成,分别用于实现正弦波、方波、三角波,具体算法在2.2中已经给出。

voidCSound::FillBuf(SOUNDTYPE soundtype, intfreq, char amp, float phase) { doublefAngle=0.0; int i;

if(amp>127)amp=127; if(amp<0)amp=0; switch(soundtype){

case ST_SIN: //生成正弦波

for(i=0;i

{ buf[i]=(char)(amp*sin(fAngle+phase)); fAngle+=2*PI*freq/SAMPLE_RATE; if(fAngle>2*PI)fAngle-=2*PI; }

break;

case ST_SQUARE: //生成方波 for(i=0;i

{ buf[i]=(char)(amp*sin(fAngle+phase)); if(buf[i]>0)buf[i]=amp; elsebuf[i]=-amp;

fAngle+=2*PI*freq/SAMPLE_RATE; if(fAngle>2*PI)fAngle-=2*PI; }

break;

case ST_TRIANGLE: //生成三角波 double x=phase/2/PI; x=x-(int)x;

for(i=0;i

{ if(x>=0&&x<=0.5) buf[i]=(char)(amp*(1-4*x)); else buf[i]=(char)(amp*(4*x-3)); x+=(double)freq/SAMPLE_RATE; if(x>1)x-=1; }

break; } }

另外还有几个主要成员函数是GenFreq、StopGen、CloseDevice,分别用于开始发声、停止发声、关闭音频设备以释放资源。其中CloseDevice在析构函数中被调用。

3.2.2. CPlayButton类

设计CPlayButton类的目的是响应鼠标按下与鼠标松开两个消息,因为MFC中直接使用CButton类是不能单独响应鼠标按下与鼠标松开两个消息的。因此在该类中添加了两个消息响应函数OnLButtonDown和OnLButtonUp。

由于该类的对象都被初始化为CDigitPianoDlg的子窗口,故在两个新的成员函数中用GetParent获得父类对象指针。另外,在CDigitPianoDlg类中,定义了一个CSound类型的成员变量m_sound,所以可以两个新的成员函数可以访问m_sound。下面给出代码。

在OnLButtonDown中加入以下代码。

CDigitPianoDlg *pParent=(CDigitPianoDlg *)GetParent();

pParent->m_sound.FillBuf(pParent->m_soundtype,pParent->m_frequency,

pParent->m_amp,(float)pParent->m_phase);

pParent->m_sound.GenFreq(); pParent->Invalidate();

在OnLButtonUp中加入以下代码。

((CDigitPianoDlg *)GetParent())->m_sound.StopGen();

可以看出,按钮按下时调用GenFreq发声,松开时调用StopGen停止发声。该类的对象对应于图 3中的“播放”按钮。

由于CSoundButton是以CPlayButton为基类,且将会重载其中一个成员函数,为了程序便于扩充,将两个新增成员函数都申明为虚函数。

3.2.3. CSoundButton类

该类是为动态生成音键的需要而定义的。由于也要响应鼠标按下与鼠标松开两个消息,故以CPlayButton类为基类。由于每个音键对应一个频率,故加入无符号整型成员变量m_frequency,相应的需要加入SetFrequency函数来为该变量赋值。另外,需要重载OnLButtonDown函数,主要是将FillBuf中的频率参数从pParent->m_frequency改为m_frequency,从而发出自己的频率对应的声音。

3.3. 主控程序的实现

主控程序主要包含三部分内容,初始化、参数输入和图形显示,都是在CDigitPianoDlg类中实现的。

(1)初始化。在OnInitDialog函数中,加入以下代码。可以看出功能是音频设备的初始化、部分变量赋初始值和动态创建音键

m_sound.PrepareDevice(WAVE_MAPPER);

m_SoundButtons=new CSoundButton[NUM_OF_SOUND_BTN]; for(int i=0;i

str.Format(\

m_SoundButtons[i].Create(str,WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,

CRect(25*i,10,25+25*i,100),this,8888+i); m_SoundButtons[i].SetFrequency(FreqTable[i]); }

((CComboBox *)GetDlgItem(IDC_TYPE))->SetCurSel(0); UpdateData(FALSE); m_soundtype=ST_SIN;

(2)参数输入。通过ClassWizard为每个输入框与下拉框关联一个成员变量,从而实现界面与后台的数据交换。这样做不仅方便了数据交换,也可以方便的限制非法的输入(如在数值框中输入字符或者输入数值不在合法范围内)。

(3)图形显示。在界面设计时用到了图形显示区,该区域其实是一个pic框控件,ID为ID_WAVEOUT,故图形的显示就是先获得显示区大小,然后在该区域中显示buf中的数据。具体的实现可以先在OnDigitPianoDlg中加入如下函数。

voidCDigitPianoDlg::PlotSoundBuf() {

CClientDCdc(GetDlgItem(IDC_WAVEOUT)); CRectrect;

char *buf=m_sound.buf;

GetDlgItem(IDC_WAVEOUT)->GetWindowRect(rect); int Height=rect.Height(); dc.MoveTo(0,Height/2); const float inv=1.0;

for(int i=(int)inv;i<(int)(rect.Width()/inv);i++)

dc.LineTo((int)(inv*i),(int)(buf[i]/150.*Height/2.)+Height/2);

}

然后在OnPaint函数中调用PlotSoundBuff函数。

4. MATLAB编程实现及其与VC实现的对比

4.1. 使用GUIDE设计界面

MATLAB为了方便界面了设计,提供了GUIDE工具,其使用与VB、VC等的可视化编程类似。根据功能需求,设计如图 4所示的界面。其中含“axes1”的区域是绘图区。可以看出,与MFC做的界面相比,多了一个“默认值”按钮,原因是方便快速输入初值。在波形下拉框中添加了正弦波、方波、三角波、锯齿波、白噪声五项。

4.2. 后台程序设计

4.2.1. 参数的存储

为了将用户输入的参数存储起来以备其它函数的使用,这里采用handles结构体,因为该结构体在该程序的所有函数中都能够访问到,故可以作为全局变量使用。一般地,存储参数需要用到以下代码

handles.XXX=YYY %XXX存储了YYY的内容 guidata(hObject,handles); %保存存储结果 使用存储的内容时可直接引用handles.XXX。

图 4 使用GUIDE设计的界面

4.2.2. playsound函数

考虑到数字信号发生器与数字电子琴都会用到这样一个模块,也就是输入波形、频率、幅值、相位,输出声音和波形图。所以为了避免代码重复,需要自定义一个名为playsound的函数,其代码如下。

functionplaysound(soundtype,frequency,amp,phase) Fs=41000; %设置采样频率 x=[0:1/Fs:1]; switchsoundtype

case 1 %正弦波

y=amp*sin(2*pi*x*frequency+phase); case 2 %方波

y=amp*sign(sin(2*pi*x*frequency+phase)); case 3 %三角波

y=amp*sawtooth(2*pi*x*frequency+phase,0.5); case 4 %锯齿波

联系合同范文客服:xxxxx#qq.com(#替换为@)