C++的简单声音
Simple sounds in C++
在Visual Studio 2012中使用C++11,我正在尝试在Pascal中观察到的声音。 在 Pascal 中,您似乎能够向内部扬声器发送一个频率,该扬声器播放此频率,直到您告诉它停止(或直到您告诉它播放不同的频率)。 所以这是我需要的:
- 我必须能够指定声音的频率
- 声音必须有很小或没有间隙(最多 5 毫秒是可以接受的)
- 我不想使用外部声音库(请不要浪费我的时间建议它们,除非它们非常轻巧并且提供了非常广泛的使用范围)
- 最好在内置扬声器上播放声音,而不是通过计算机的常规扬声器播放
我在 Visual Studio 中找不到任何可包含的库/标头,它们提供了将波形发送到内部扬声器的能力。 我愿意尝试直接与内置扬声器合作(我知道这很难,但我不是白痴 - 我想我可以弄清楚,有一些指导),但我找不到任何关于在 Windows 中访问内部扬声器的文档。
编辑:从这篇文章中,我能够收集到现在大多数计算机实际上没有内置扬声器。 无赖。 不过没关系 - 我可以使用连接的扬声器,但我仍然有以下要求:
- 我需要能够指定一个频率并让扬声器播放该频率,直到我告诉他们停止
- 我宁愿不使用外部库
编辑2:这是我正在研究的课程:
#define HALF_NOTE 1.059463094359 // HALF_NOTE ^ 12 = 2
#include <Windows.h>
#include <math.h>
class SoundEffect
{
public:
SoundEffect(){}
void Play()
{
for (int i = 0; data[i + 1] > 0; i++)
{
Beep(16 * pow(HALF_NOTE, data[i++] - 1), data[i] * 10); // (frequency of c0) * (twelfth root of 2) ^ (number of half steps above c0)
// Ideally, the code would look more like this (pseudocode):
// sound(16 * pow(HALF_NOTE, data[i++] - 1)); // Start playing the specified frequency
// delay(data[i] * 10);
}
// nosound();
}
int& operator[] (int location) { return data[location]; }
private:
int data[256];
};
不久前我搜索了类似的东西(生成简单的声音),并找到了这些可以完成这项工作的库:
- 端口音频
- Nsound
- 合成工具包 (STK)
不过,我没有时间尝试比较它们。玩得开心:)
我最终使用 Windows 多媒体 API 创建波形并将它们发送到声音设备。 我的解决方案基于这里的教程。 这是我最终得到的:
#define HALF_NOTE 1.059463094359 // HALF_NOTE ^ 12 = 2
#define PI 3.14159265358979
#include <Windows.h>
#include <math.h>
using namespace std;
class SoundEffect
{
public:
SoundEffect()
{
m_data = NULL;
}
SoundEffect(const int noteInfo[], const int arraySize)
{
// Initialize the sound format we will request from sound card
m_waveFormat.wFormatTag = WAVE_FORMAT_PCM; // Uncompressed sound format
m_waveFormat.nChannels = 1; // 1 = Mono, 2 = Stereo
m_waveFormat.wBitsPerSample = 8; // Bits per sample per channel
m_waveFormat.nSamplesPerSec = 11025; // Sample Per Second
m_waveFormat.nBlockAlign = m_waveFormat.nChannels * m_waveFormat.wBitsPerSample / 8;
m_waveFormat.nAvgBytesPerSec = m_waveFormat.nSamplesPerSec * m_waveFormat.nBlockAlign;
m_waveFormat.cbSize = 0;
int dataLength = 0, moment = (m_waveFormat.nSamplesPerSec / 75);
double period = 2.0 * PI / (double) m_waveFormat.nSamplesPerSec;
// Calculate how long we need the sound buffer to be
for (int i = 1; i < arraySize; i += 2)
dataLength += (noteInfo[i] != 0) ? noteInfo[i] * moment : moment;
// Allocate the array
m_data = new char[m_bufferSize = dataLength];
int placeInData = 0;
// Make the sound buffer
for (int i = 0; i < arraySize; i += 2)
{
int relativePlaceInData = placeInData;
while ((relativePlaceInData - placeInData) < ((noteInfo[i + 1] != 0) ? noteInfo[i + 1] * moment : moment))
{
// Generate the sound wave (as a sinusoid)
// - x will have a range of -1 to +1
double x = sin((relativePlaceInData - placeInData) * 55 * pow(HALF_NOTE, noteInfo[i]) * period);
// Scale x to a range of 0-255 (signed char) for 8 bit sound reproduction
m_data[relativePlaceInData] = (char) (127 * x + 128);
relativePlaceInData++;
}
placeInData = relativePlaceInData;
}
}
SoundEffect(SoundEffect& otherInstance)
{
m_bufferSize = otherInstance.m_bufferSize;
m_waveFormat = otherInstance.m_waveFormat;
if (m_bufferSize > 0)
{
m_data = new char[m_bufferSize];
for (int i = 0; i < otherInstance.m_bufferSize; i++)
m_data[i] = otherInstance.m_data[i];
}
}
~SoundEffect()
{
if (m_bufferSize > 0)
delete [] m_data;
}
SoundEffect& operator=(SoundEffect& otherInstance)
{
if (m_bufferSize > 0)
delete [] m_data;
m_bufferSize = otherInstance.m_bufferSize;
m_waveFormat = otherInstance.m_waveFormat;
if (m_bufferSize > 0)
{
m_data = new char[m_bufferSize];
for (int i = 0; i < otherInstance.m_bufferSize; i++)
m_data[i] = otherInstance.m_data[i];
}
return *this;
}
void Play()
{
// Create our "Sound is Done" event
m_done = CreateEvent (0, FALSE, FALSE, 0);
// Open the audio device
if (waveOutOpen(&m_waveOut, 0, &m_waveFormat, (DWORD) m_done, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR)
{
cout << "Sound card cannot be opened." << endl;
return;
}
// Create the wave header for our sound buffer
m_waveHeader.lpData = m_data;
m_waveHeader.dwBufferLength = m_bufferSize;
m_waveHeader.dwFlags = 0;
m_waveHeader.dwLoops = 0;
// Prepare the header for playback on sound card
if (waveOutPrepareHeader(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
cout << "Error preparing Header!" << endl;
return;
}
// Play the sound!
ResetEvent(m_done); // Reset our Event so it is non-signaled, it will be signaled again with buffer finished
if (waveOutWrite(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
cout << "Error writing to sound card!" << endl;
return;
}
// Wait until sound finishes playing
if (WaitForSingleObject(m_done, INFINITE) != WAIT_OBJECT_0)
{
cout << "Error waiting for sound to finish" << endl;
return;
}
// Unprepare our wav header
if (waveOutUnprepareHeader(m_waveOut, &m_waveHeader,sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
cout << "Error unpreparing header!" << endl;
return;
}
// Close the wav device
if (waveOutClose(m_waveOut) != MMSYSERR_NOERROR)
{
cout << "Sound card cannot be closed!" << endl;
return;
}
// Release our event handle
CloseHandle(m_done);
}
private:
HWAVEOUT m_waveOut; // Handle to sound card output
WAVEFORMATEX m_waveFormat; // The sound format
WAVEHDR m_waveHeader; // WAVE header for our sound data
HANDLE m_done; // Event Handle that tells us the sound has finished being played.
// This is a very efficient way to put the program to sleep
// while the sound card is processing the sound buffer
char* m_data; // Sound data buffer
int m_bufferSize; // Size of sound data buffer
};
相当复杂,但它有效。 我把它和这样的文本文件一起使用(DOS mario & luigi的音效):
LifeMusic 56 8 61 8 65 8 61 8 63 8 68 8
GrowMusic 37 4 44 4 49 4 38 4 45 4 50 4 39 4 46 4 51 4
CoinMusic 66 1
PipeMusic 13 0 13 8 1 0 1 16 13 0 13 8 1 0 1 16 13 0 13 8 1 0 1 16
FireMusic 41 1 46 1
HitMusic 25 2 13 3 1 4 25 1 13 2 1 3
DeadMusic 25 3 13 4 1 6
NoteMusic 1 3 13 4 1 6
StarMusic 37 4 41 4 44 4 49 4 53 4 56 4 61 4 65 4 68 4 73 4
为了简要概述一下,我的主要文件在这些行中阅读。 对于每一行,它从整数数组创建一个声音效果,并创建一个映射,其中键是声音效果的名称,值是创建的SoundEffect
实例。
在文本文件中,每行应具有偶数个整数。 如果将一行整数分成对,第一个数字将是 A1 上方半步的数量(以确定频率),第二个数字将是持续时间,以 75 秒为单位(任意,我知道)。
- 在c++中用vector填充一个简单的动态数组
- (C++)分析树以计算返回错误值的简单算术表达式
- 我的简单if-else语句是如何无法访问的代码
- 使用简单类型列表实现的指数编译时间.为什么
- 如何在BST的这个简单递归实现中消除警告
- Xaudio2在更改缓冲区或循环时弹出声音
- 一种在C++中读取TXT配置文件的简单方法
- 关于简单C++函数(is_palindrome)的逻辑的问题
- 显示错误输出的简单数组排序程序
- 当无法使用模板和宏时,生成类型变体C++代码的最简单方法是什么?
- 退出简单while循环时出现问题
- 为什么简单的算术减法在"if"条件下不起作用?
- 如何更改'a'声音?
- C++-字符串是否包含一个带有简单循环的单词
- 关于 c++ 函数中指针赋值的简单问题
- 有没有一种简单的方法可以使用SFML在后台线程中播放声音
- 简单的Qt视频流应用程序,只有声音,没有视频(白屏)
- 播放 C/C++ 声音的最简单方法
- C++的简单声音
- 简单的C++声音API