C++ Beep()演奏简谱的改进以及实现背景音乐

简介: C++ Beep()演奏简谱的改进以及实现背景音乐

看到Beep()就会想起上世纪90年代初在8086的机器或者稍后的286、386机器上用解释型Basic编简谱玩的情景,这便是那个声霸卡还没上市的年代里,几乎是人们在PC上唯一可编的声音了。


   Beep的函数原型:


BOOL Beep(
    DWORD dwFreq;  /*指定要发出的频率(HZ)*/
    DWORD dwDuration;  /*指定发音的时长,以毫秒为单位*/
    );

通常先把do re mi ...的频率预定义好,再照着简谱把频率和时长编入两个数组或一个结构体数组。

#define do 523
#define re 578
#define mi 659
#define fa 698
#define so 784
#define la 880
#define si 988
......
int freq[] = { do,re,mi,do,do,re,mi,do,...}
int duration[] = {300,300,300,300,300,300,300,300,...}

最后用Beep()循环输出。


对此我做了些小改进:把频率定入一个结构体数组便于反复调用;简谱放入一个vector容器就不用管谱子的长短不用管数组的下标,只要按顺序push_back()每一个音符,还能随时插入删除某个音符,重复的小节可以用循环多次输出以缩短代码长度;简谱转代码时“所见即所得”看到什么数字就写入什么数字;音符的时长也以“拍”为单位,每个小节后加个空行便于检查每节的总时长。源代码如下:


#include <array>
#include <vector>
#include <iostream>
#include <windows.h>
using namespace std;
#define PAI 300  //一拍的时长,可以自行调整 
struct tone{
  short a; short d; short g;  //中音;低音;高音 
  short b; short e; short f;  //半阶音 
};
struct tune{
  short t;  //唱名 
  float l;  //音长 
  short b;  //音调 
};
array<tone,8>m;
short t(short a,short b)
{
  switch(b){
    case 0: return m[a].a;
    case 1: return m[a].d;
    case 2: return m[a].g;
    case 3: return m[a].b;
    case 4: return m[a].e;
    case 5: return m[a].f;
  }
}
short p(float p)
{
  return (short)(PAI*p);
}
void initTone(void)
{ //把各音符的频率写入数组,一劳永逸可以随时调用
  m.at(0)={0,0,0,0,0,0};
  m.at(1)={523,262,1046,554,277,1109};
  m.at(2)={578,294,1175,622,311,1245};
  m.at(3)={659,330,1318,659,330,1318};
  m.at(4)={698,349,1493,740,370,1556};
  m.at(5)={784,392,1568,831,415,1661};
  m.at(6)={880,440,1760,932,466,1865};
  m.at(7)={988,494,1976,988,494,1976};
}
void initTune(vector<tune>&s)
{
  for (int i=0;i<2;i++){  
      s.push_back({1,1,0});
      s.push_back({2,1,0});
      s.push_back({3,1,0});
      s.push_back({1,1,0});
    }
  for (int i=0;i<2;i++){  
      s.push_back({3,1,0});
      s.push_back({4,1,0});
      s.push_back({5,2,0});
    }
    for (int i=0;i<2;i++){  
      s.push_back({5,0.75,0});
      s.push_back({6,0.25,0});
      s.push_back({5,0.75,0});
      s.push_back({4,0.25,0});
      s.push_back({3,1,0});
      s.push_back({1,1,0});
    }
  for (int i=0;i<2;i++){  
      s.push_back({1,1,0});
      s.push_back({5,1,1});
      s.push_back({1,2,0});
    }
  //以上根据简谱上的拍子和音调编入容器,方法如下: 
  //第一个参数 1~7 对应do re mi fa so la si 0=休止符
  //第二个参数 1拍=1;半拍=0.5 四分之一拍=0.25 以此类推 
  //第三个参数 一般就为0,低音=1 高音=2 对应的半阶音=3 4 5 
  //转简谱时看到什么数字就是什么,不用记频率数方便编辑和排错 
}
int main()
{
  vector<tune>music;
  initTone();
  initTune(music);
  cout<<"开始演奏《两只老虎》"<<endl; 
  for (auto m:music) Beep(t(m.t,m.b),p(m.l));
  return 0; 
}



背景音乐的实现


一个程序独占控制台CPU时间来演奏音乐,没有一点实用性。我们再来编一首《送别》并实现“后台演奏”,注意:碰到简谱中有重复的小节可多放几个子函数以供多次调用。


实际上,演奏的同时还要做其他工作,就是要创建多个线程来完成几个不同的工作:先调用<pthread.h>库函数pthread_create()创建一个线程来播放背景音乐,然后让主程序开始做其它工作,并且可以按需要来选择哪项任务先结束。源代码如下:

#include <array>
#include <vector>
#include <iostream>
#include <ctime>
#include <sstream>
#include <windows.h>
#include <pthread.h>
using namespace std;
#define PAI 400  //一拍的时长,可以自行调整 
struct tone{
  short a; short d; short g;  //中音;低音;高音 
  short b; short e; short f;  //半阶音 
};
struct tune{
  short t;  //唱名 
  float l;  //音长 
  short b;  //音调 
};
array<tone,8>m;
short t(short a,short b)
{
  switch(b){
    case 0: return m[a].a;
    case 1: return m[a].d;
    case 2: return m[a].g;
    case 3: return m[a].b;
    case 4: return m[a].e;
    case 5: return m[a].f;
  }
}
short p(float p)
{
  return (short)(PAI*p);
}
void initTone(void)
{ 
  m.at(0)={0,0,0,0,0,0};
  m.at(1)={523,262,1046,554,277,1109};
  m.at(2)={578,294,1175,622,311,1245};
  m.at(3)={659,330,1318,659,330,1318};
  m.at(4)={698,349,1493,740,370,1556};
  m.at(5)={784,392,1568,831,415,1661};
  m.at(6)={880,440,1760,932,466,1865};
  m.at(7)={988,494,1976,988,494,1976};
}
void repeat1(vector<tune>&s)
{
  s.push_back({5,1,0});
  s.push_back({3,0.5,0});
  s.push_back({5,0.5,0});
}
void repeat2(vector<tune>&s)
{
  s.push_back({6,1,0});
  s.push_back({1,1,2});
  s.push_back({5,2,0});
}
void repeat3(vector<tune>&s, short i)
{
  s.push_back({i,2,0});
  s.push_back({0,1,0});
  s.push_back({0,1,0});
}
void initTune(vector<tune>&s)
{
  for (int i=0;i<2;i++){
    repeat1(s);
    s.push_back({1,2,2});
    repeat2(s);
    s.push_back({5,1,0});
    s.push_back({1,0.5,0}); 
    s.push_back({2,0.5,0});
    s.push_back({3,1,0});
    s.push_back({2,0.5,0});
    s.push_back({1,0.5,0});
    repeat3(s,2);
    repeat1(s);
    s.push_back({1,1.5,2});
    s.push_back({7,0.5,0}); 
    repeat2(s); 
    s.push_back({5,1,0});
    s.push_back({2,0.5,0}); 
    s.push_back({3,0.5,0});
    s.push_back({4,1.5,0});
    s.push_back({7,0.5,1});
    repeat3(s,1);
    s.push_back({6,1,0});
    s.push_back({1,1,2});
    s.push_back({1,2,2});
    s.push_back({7,0.5,0});
    s.push_back({6,0.5,0});
    s.push_back({7,0.5,0});
    s.push_back({1,2,2});   
    s.push_back({6,0.5,0});
    s.push_back({7,0.5,0});
    s.push_back({1,0.5,2});
    s.push_back({6,0.5,0});
    s.push_back({6,0.5,0});
    s.push_back({5,0.5,0});
    s.push_back({3,0.5,0});
    s.push_back({1,0.5,0});
    repeat3(s,2);
    repeat1(s);
    s.push_back({1,1.5,2});
    s.push_back({7,0.5,0}); 
    repeat2(s); 
    s.push_back({5,1,0});
    s.push_back({2,0.5,0});
    s.push_back({3,0.5,0});
    s.push_back({4,1.5,0});
    s.push_back({7,0.5,1});
      repeat3(s,1);
  }
}
void gotoXY(short x, short y)
{
    COORD position = {x, y};
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hConsole, position);
}
bool play_not_end=true;  //设置演奏标记 
void* play(void* args)
{
  vector<tune>music;
  initTone();
  initTune(music);
  gotoXY(28,8);
  cout<<"背景音乐:《送别》";
  for (auto v:music)
    if (play_not_end) Beep(t(v.t,v.b),p(v.l));
    else break;
  play_not_end=false;
}
char strTime[20];
void* coutTime(void* args)
{
  time_t lt;
  while (play_not_end){
    time(&lt);
    strftime(strTime,sizeof(strTime),"%Y-%m-%d %H:%M:%S",localtime(&lt));
    Sleep(300);
  }
  play_not_end=false;
}
int main()
{
  pthread_t pt;
  int ret = pthread_create(&pt, NULL, play, NULL);  //创建一个线程,play()变相成为背景音乐 
  if (ret!=0) cout<<"create thread_1 error: error_code="<<ret<<endl;
  ret = pthread_create(&pt, NULL, coutTime, NULL);  //再创建一个线程,用于显示当前系统时间 
  if (ret!=0) cout<<"create thread_2 error: error_code="<<ret<<endl;
  //在 pthread_create()与 pthread_exit()之间,便是程序工作的主场 
  Sleep(100);
  gotoXY(28,10);
  cout<<"工作开始:循环次数 ";
  for(int i=1;play_not_end;i++){
    gotoXY(48,10);
    cout<<i<<endl;
    gotoXY(60,0);
    cout<<strTime; //显示另外一个线程取回的时间 
    Sleep(300);
    if (i==100) play_not_end=false;  //工作在音乐演奏完之前结束,只要把标记设为false即可 
  }
  gotoXY(28,12);
  cout<<"工作结束!"<<endl;
  pthread_exit(NULL);
  return 0; 
}


注意:本程序在Dev-C++ 5.11/编译器TDM-GCC 4.9.2 64-bit 上通过编译,若用VS编译找不到头文件<pthread.h>,请搜索“如何在vs2017上使用pthread.h”并自行安装库文件。

 

附:《两只老虎》、《送别》简谱


20210205000119845.png


20210207192342193.png

目录
相关文章
|
1月前
|
自然语言处理 搜索推荐 Docker
CosyVoice实现声音复刻
这篇文章介绍了如何使用CosyVoice平台实现个性化的声音复刻,包括录制样本音频、上传处理以及生成定制化语音的全流程。
381 6
CosyVoice实现声音复刻
|
3月前
黑神话:悟空中的音效设计与音乐制作
【8月更文第26天】在《黑神话:悟空》这款游戏中,音效和背景音乐是构建沉浸式游戏体验的重要组成部分。本文将探讨游戏音效和背景音乐的创作过程,以及它们如何增强游戏氛围。
391 0
|
5月前
|
数据安全/隐私保护 计算机视觉
图像处理之玻璃水印特效(祝大家圣诞节快乐)
图像处理之玻璃水印特效(祝大家圣诞节快乐)
41 8
|
5月前
|
图形学
【unity小技巧】实现FPS射击游戏枪武器随镜头手臂摇摆效果
【unity小技巧】实现FPS射击游戏枪武器随镜头手臂摇摆效果
50 0
7-1 射击游戏
7-1 射击游戏
104 0
|
前端开发 JavaScript
2023跨年代码(烟花+背景音乐)
2023跨年代码(烟花+背景音乐)
332 0
An动画基础之元件的影片剪辑效果
An动画基础之元件的影片剪辑效果
415 0
An动画基础之元件的影片剪辑效果
88.播放声音和音效
88.播放声音和音效
259 0
88.播放声音和音效
|
JavaScript
💃接着奏乐!接着舞!让文字随着音乐跃动吧!
💃接着奏乐!接着舞!让文字随着音乐跃动吧!
203 0
|
前端开发
用canvas让美女沉浸在音符的海洋里
用canvas让美女沉浸在音符的海洋里
用canvas让美女沉浸在音符的海洋里