3

Visual Studio 2012 で C++ 11 を使用して、Pascal で観察したサウンドを試しています。Pascal では、内部スピーカーに周波数を送信できるようです。内部スピーカーは、停止するように指示するまで (または別の周波数を再生するように指示するまで)、この周波数を再生します。だからここに私が必要なものがあります:

  • 音の周波数を特定できなければならない
  • サウンドにはギャップがほとんどまたはまったくない必要があります (最大 5 ミリ秒まで許容されます)。
  • 外部のサウンド ライブラリを使用したくありません (非常に軽量で非常に幅広い用途を提供するものでない限り、外部のサウンド ライブラリを提案して時間を無駄にしないでください)
  • サウンドは、コンピューターの通常のスピーカーではなく、内部スピーカーで再生されることをお勧めします。

内部スピーカーに波形を送信する機能を提供する、ビジュアル スタジオのインクルード可能なライブラリ/ヘッダーが見つかりません。内蔵スピーカーを直接操作してみます (これが難しいことはわかっていますが、私はばかではありません。いくつかのガイダンスがあれば、理解できると思います)。しかし、ドキュメントが見つかりません。 Windows の内蔵スピーカーへのアクセスについて。

編集:この投稿から、最近のほとんどのコンピューターには実際にはスピーカーが内蔵されていないことがわかりました。残念。それでも問題ありません。接続されたスピーカーを使用できますが、次の要件がまだあります。

  • 周波数を指定して、停止するように指示するまでスピーカーにその周波数を再生させる必要があります
  • 外部ライブラリを使用したくない

EDIT 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];
};
4

2 に答える 2

1

最終的に、Windows Multimedia 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 マリオとルイージの効果音) で使用します。

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インスタンスであるマップを作成します。

テキスト ファイルでは、各行に偶数の整数が含まれている必要があります。整数の 1 行をペアに分割すると、最初の数字は A1 より上の半音の数 (周波数を決定するため) になり、2 番目の数字は 1 秒の 75 秒単位 (任意、I知る)。

于 2013-11-11T06:42:36.590 に答える