【问题标题】:Simple sounds in C++C++ 中的简单声音
【发布时间】:2013-11-10 19:55:42
【问题描述】:

在 Visual Studio 2012 中使用 C++ 11,我正在尝试在 Pascal 中观察到的声音。在 Pascal 中,您似乎可以向内部扬声器发送一个频率,该扬声器会播放该频率,直到您告诉它停止(或直到​​您告诉它播放不同的频率)。所以这就是我需要的:

  • 我必须能够指定声音的频率
  • 声音必须有很小或没有间隙(最多 5 毫秒是可以接受的)
  • 我不想使用外部声音库(请不要浪费我的时间推荐它们,除非它们非常轻量级并且提供非常广泛的使用范围)
  • 最好在内部扬声器上播放声音,而不是通过计算机的常规扬声器播放

我在 Visual Studio 中找不到任何能够将波形发送到内部扬声器的可包含库/头文件。我愿意尝试直接与内部扬声器合作(我知道这会很困难,但我不是白痴 - 我想我可以通过一些指导来弄清楚),但我找不到任何文档在 Windows 中访问内置扬声器。

编辑:从this post,我了解到现在大多数计算机实际上并没有内置扬声器。真可惜。不过没关系 - 我可以使用连接的扬声器,但我仍然有以下要求:

  • 我需要能够指定一个频率并让扬声器播放该频率,直到我告诉他们停止
  • 我宁愿不使用外部库

编辑 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];
};

【问题讨论】:

  • 类似Beep?
  • 过去,这些声音不是“直接在声卡上”播放的,而是来自内部扬声器。在不再使用 Pascal 之前,PC 大多没有声卡。
  • 机器没有扬声器了。猜猜为什么 Beep() 仍然有效 :)
  • Beep() 不起作用,因为音调之间的间隔太长(请参阅this post)。我想我可以尝试使用 Beep() 和线程来消除差距的一些非常 hacky 的解决方案,但除了这将是多么荒谬的复杂之外,我不确定这是否可行,我不想浪费很多时间在一些可能行不通的事情上。

标签: c++ audio


【解决方案1】:

不久前我搜索了类似的东西(生成简单的声音),发现这些库可以完成这项工作:

不过,我还没有时间尝试比较它们。玩得开心:)

【讨论】:

    【解决方案2】:

    我最终使用 Windows 多媒体 API 创建波形并将它们发送到声音设备。我的解决方案基于教程here。这是我最终得到的结果:

    #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 秒为单位(任意,I知道)。

    【讨论】:

    • 我认为您应该在问题中包含一个 标签,因为选择的解决方案是特定于操作系统的。我正在寻找类似的东西在 Linux 上工作。
    猜你喜欢
    • 2012-04-16
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多