基于C怎么编写一个键盘提示音程序
更新时间:2023-11-07
首先,我们需要使用C++中的Win32 API函数来控制计算机的音频。为了播放声音,我们将使用Waveform Audio接口(简称WAV)。
第一段落
下面是使用Win32 API的代码段来初始化音频设备,并设置使用的WAV格式。我们可以使用WAVEFORMATEX结构来设置需要使用的WAV格式。这个结构定义了声音数据位宽、采样率等音频格式相关的信息。请参考以下代码示例:
代码段 1: 初始化音频设备和WAV格式
#include <windows.h> #include <mmsystem.h> // 定义结构体: WAVEFORMATEX WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, // 音频的类型 (PCM) 1, // 声道数 8000, // 采样率 (每秒的样本数) 16, // 每个采样点的位数 (16 位) 2, // 数据块的对齐方式 0, // 数据块的附加信息 0 // 保留的信息 }; HWAVEOUT hWaveOut; // 这是音频设备的句柄 // 初始化音频设备 waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);第二段落 接下来,我们将使用一个函数来生成频率序列和持续时间序列,然后使用这些信息来创建声音数据块。有了声音数据块,我们就可以将它们传递给音频设备来播放声音。
代码段 2:生成声音数据块并播放声音
// 定义一组常数 const int SAMPLE_RATE = 8000; // WAV格式的采样率 const int AMPLITUDE = 32000; // 最大振幅 // 生成频率序列 const double FREQ_C4 = 261.63; // 中央C音 const double FREQ_E4 = 329.63; // E音 const double FREQ_G4 = 392.00; // G音 // 生成持续时间序列 (单位:毫秒) const int DURATION_QUARTER_NOTE = 500; // 四分音符长 const int DURATION_HALF_NOTE = 2 * DURATION_QUARTER_NOTE; // 二分音符长 const int DURATION_WHOLE_NOTE = 4 * DURATION_QUARTER_NOTE; // 全音符长 // 生成数据块 void GenerateSoundData(std::vector<short>& data, double frequency, int duration) { const int numSamples = SAMPLE_RATE * duration / 1000; const double delta = 2 * M_PI * frequency / SAMPLE_RATE; data.resize(numSamples); for (int i = 0; i < numSamples; i++) { short sample = (short)(AMPLITUDE * sin(i * delta)); data[i] = sample; } } // 播放音符 void PlayNote(double frequency, int duration) { std::vector<short> data; GenerateSoundData(data, frequency, duration); // 创建声音数据块 WAVEHDR header; memset(&header, 0, sizeof(header)); header.lpData = (LPSTR)data.data(); header.dwBufferLength = data.size() * sizeof(short); // 准备音频数据块 waveOutPrepareHeader(hWaveOut, &header, sizeof(header)); // 发送音频数据块到设备来播放声音 waveOutWrite(hWaveOut, &header, sizeof(header)); // 等待音频数据块完成 while (header.dwFlags & WHDR_BUSY) Sleep(10); // 释放音频数据块 waveOutUnprepareHeader(hWaveOut, &header, sizeof(header)); }第三段落 现在,在获得每个音符的频率和持续时间后,我们可以将其转换为指示音符的符号,“do”、“re”,等等。以下代码段展示了如何实现这个功能:
代码段 3: 将音高和持续时间转换为符号
// 将音高转换为符号 std::string PitchToSymbol(double pitch) { int semitones = (int)round(12 * log(pitch / FREQ_C4) / log(2)); switch (semitones) { case 0: return "do"; case 2: return "re"; case 4: return "mi"; case 5: return "fa"; case 7: return "so"; case 9: return "la"; case 11: return "xi"; default: return "-"; } } // 将持续时间转换为符号 std::string DurationToSymbol(int duration) { if (duration == DURATION_QUARTER_NOTE) return "4"; if (duration == DURATION_HALF_NOTE) return "2"; if (duration == DURATION_WHOLE_NOTE) return "1"; else return ""; }第四段落 组合所有的部分,我们就可以编写一个完整的键盘提示音程序了。请参考下面这个基于控制台的键盘提示音程序的代码示例:
代码段 4: 完整的键盘提示音程序
#include <iostream> #include <vector> #include <string> #include <windows.h> #include <mmsystem.h> WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, // 音频的类型 (PCM) 1, // 声道数 8000, // 采样率 (每秒的样本数) 16, // 每个采样点的位数 (16 位) 2, // 数据块的对齐方式 0, // 数据块的附加信息 0 // 保留的信息 }; const int SAMPLE_RATE = 8000; // WAV格式的采样率 const int AMPLITUDE = 32000; // 当前音乐的最大振幅 const double FREQ_C4 = 261.63; // 中央C音 const double FREQ_E4 = 329.63; // E音 const double FREQ_G4 = 392.00; // G音 const int DURATION_QUARTER_NOTE = 500; // 四分音符长 const int DURATION_HALF_NOTE = 2 * DURATION_QUARTER_NOTE; // 二分音符长 const int DURATION_WHOLE_NOTE = 4 * DURATION_QUARTER_NOTE; // 全音符长 HWAVEOUT hWaveOut; // 这是音频设备的句柄 // 生成数据块 void GenerateSoundData(std::vector<short>& data, double frequency, int duration) { const int numSamples = SAMPLE_RATE * duration / 1000; const double delta = 2 * M_PI * frequency / SAMPLE_RATE; data.resize(numSamples); for (int i = 0; i < numSamples; i++) { short sample = (short)(AMPLITUDE * sin(i * delta)); data[i] = sample; } } // 播放音符 void PlayNote(double frequency, int duration) { std::vector<short> data; GenerateSoundData(data, frequency, duration); // 创建声音数据块 WAVEHDR header; memset(&header, 0, sizeof(header)); header.lpData = (LPSTR)data.data(); header.dwBufferLength = data.size() * sizeof(short); // 准备音频数据块 waveOutPrepareHeader(hWaveOut, &header, sizeof(header)); // 发送音频数据块到设备来播放声音 waveOutWrite(hWaveOut, &header, sizeof(header)); // 等待音频数据块完成 while (header.dwFlags & WHDR_BUSY) Sleep(10); // 释放音频数据块 waveOutUnprepareHeader(hWaveOut, &header, sizeof(header)); } // 将音高转换为符号 std::string PitchToSymbol(double pitch) { int semitones = (int)round(12 * log(pitch / FREQ_C4) / log(2)); switch (semitones) { case 0: return "do"; case 2: return "re"; case 4: return "mi"; case 5: return "fa"; case 7: return "so"; case 9: return "la"; case 11: return "xi"; default: return "-"; } } // 将持续时间转换为符号 std::string DurationToSymbol(int duration) { if (duration == DURATION_QUARTER_NOTE) return "4"; if (duration == DURATION_HALF_NOTE) return "2"; if (duration == DURATION_WHOLE_NOTE) return "1"; else return ""; } // 播放键盘音符序列 void PlayKeyboardNotes(const std::vector<double>& notes, const std::vector<int>& durations) { std::cout << "\n开始弹奏\n"; for (int i = 0; i < notes.size(); i++) { std::string pitchSymbol = PitchToSymbol(notes[i]); std::string durationSymbol = DurationToSymbol(durations[i]); std::cout << pitchSymbol << durationSymbol << "\n"; PlayNote(notes[i], durations[i]); Sleep(durations[i]); } std::cout << "\n结束弹奏\n"; } // 测试代码 int main() { // 初始化音频设备 waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL); // C(四分音符) // E(四分音符) // G(四分音符) std::vector<double> notes = { FREQ_C4, FREQ_E4, FREQ_G4 }; std::vector<int> durations = { DURATION_QUARTER_NOTE, DURATION_QUARTER_NOTE, DURATION_QUARTER_NOTE }; PlayKeyboardNotes(notes, durations); // 释放音频设备 waveOutClose(hWaveOut); return 0; }总结: 通过使用Win32 API的Waveform Audio接口,我们可以使用C ++语言在Windows环境下编写键盘提示音程序。程序的关键部分包括初始化音频设备、生成声音数据块、播放声音和将音高和持续时间转换为可读的符号。我们可以根据需要自由发挥,创建不同的音符序列和节奏组合,从而实现不同的歌曲。