0

I am trying to write a program in Visual C++ that will play a sound based on volume and frequency inputs gotten from pixels of a black and white video. To start, I am first trying to get a sound to play sans parameters (which I will add in later). I've already included winmm.lib into the project.

I've got code that compiles, but does not play a sound. The code I have so far is as follows:

sound.h (provided for me, works on another project):

//file: soundthread.h
#include <Windows.h> //new
#include <mmsystem.h>

#ifndef SOUNDTHREAD_H
#define SOUNDTHREAD_H

class Sound {
    public:

        static void init();
        static void close(){waveOutReset(hWaveOut); waveOutClose(hWaveOut);};
        static void writeAudioBlock(LPSTR block);

    private:
        static HWAVEOUT hWaveOut;
};
#endif

sound.cpp (provided for me, works on another project):

#include "stdafx.h"
#include "sound.h"
#include <mmsystem.h>
#include <mmreg.h>

HWAVEOUT Sound::hWaveOut;

void Sound::init(){
    WAVEFORMATEX wfx; 
    wfx.nSamplesPerSec = 8000; 
    wfx.wBitsPerSample = 8; 
    wfx.nChannels = 1; 
    wfx.cbSize = 0; 
    wfx.wFormatTag = WAVE_FORMAT_PCM;
   // wfx.nBlockAlign = (wfx.wBitsPerSample >> 2) * wfx.nChannels;
    wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
    wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
/*
    if(waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL)
    != MMSYSERR_NOERROR) 
    //  int tt;
    //  tt = waveOutOpen(&hWaveOut, ((UINT)1), &wfx, 0, 0, CALLBACK_NULL);
    //  if(tt != MMSYSERR_NOERROR) {
            fprintf(stderr, "unable to open WAVE_MAPPER device\n");
            int tt;
            tt = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
            MessageBox(0, "unable to open WAVE_MAPPER device\n", "Error", MB_ICONERROR|MB_OK);
    /* DBG:     tt=MMSYSERR_ALLOCATED;
        tt=MMSYSERR_BADDEVICEID;
        tt=MMSYSERR_NODRIVER;
        tt=MMSYSERR_NOMEM;
        tt=WAVERR_BADFORMAT; //** this is it //
        tt=WAVERR_SYNC;  // END DBG */

//         ExitProcess(1);
//    }

}

void Sound::writeAudioBlock(LPSTR block) {
    WAVEHDR header;
    ZeroMemory(&header, sizeof(WAVEHDR));
    header.dwBufferLength = 500;
    header.lpData = block;
    waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
    waveOutWrite(hWaveOut, &header, sizeof(WAVEHDR));
    do {
        Sleep(100);
    }while(waveOutUnprepareHeader(hWaveOut,&header,sizeof(WAVEHDR)) == WAVERR_STILLPLAYING);

}

soundplay.h (created by me for holding the provided OnSound() function):

#ifndef SOUNDPLAY_H //SOUNDPLAY_H_INCLUDED
#define SOUNDPLAY_H //SOUNDPLAY_H_INCLUDED

#include "sound.h"

void OnSound(); //update this w/ freq and intensity paramaters

#endif // SOUNDPLAY_H_INCLUDED

soundplay.cpp (created by me for holding the provided OnSound() function):

#include "stdafx.h"
#include <windows.h>
#pragma comment(lib, "winmm.lib")
#include <math.h> //used to be under sound.h
#include "sound.h"
#include <mmsystem.h>
#define PI 3.141592653f
#include "soundplay.h" //new

void OnSound() 
{
    // produce a sin wave sound.
    float freq = 440.f;
    DWORD Fs=8000;
    int N=500;
    float* tt=new float[N];

    for(int i=0;i<N;i++)
    {
        tt[i]=(float)i/(float)Fs;
    }

    float intensity = 0.5f; //volume
    float *signal=new float[N];

    for(int i=0;i<N;i++)
    {
        signal[i]=intensity*sin(2.f*PI*freq*tt[i]);
    }

    BYTE* data=new BYTE[N];
    for(int i=0;i<N;i++)
    {
        data[i]=(BYTE)(signal[i]+128.f);
    }

    delete []signal;
    Sound::writeAudioBlock((LPSTR)data);
    delete []data;
    delete []tt;
}

Form1.h (code from the GUI designer containing the button that triggers OnSound to play a sound):

#include "sound.h" //new
#include "soundplay.h" //new

#pragma once

namespace a2c {

    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;

    /// <summary>
    /// Summary for Form1
    /// </summary>
    public ref class Form1 : public System::Windows::Forms::Form
    {
    public:
        Form1(void)
        {
            InitializeComponent();
            //
            //TODO: Add the constructor code here
            //
        }

    protected:
        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        ~Form1()
        {
            if (components)
            {
                delete components;
            }
        }
    private: System::Windows::Forms::Button^  button1;
    protected: 

    private:
        /// <summary>
        /// Required designer variable.
        /// </summary>
        System::ComponentModel::Container ^components;

#pragma region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        void InitializeComponent(void)
        {
            this->button1 = (gcnew System::Windows::Forms::Button());
            this->SuspendLayout();
            // 
            // button1
            // 
            this->button1->Location = System::Drawing::Point(13, 225);
            this->button1->Name = L"button1";
            this->button1->Size = System::Drawing::Size(75, 23);
            this->button1->TabIndex = 0;
            this->button1->Text = L"Sound Test";
            this->button1->UseVisualStyleBackColor = true;
            this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
            // 
            // Form1
            // 
            this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
            this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
            this->ClientSize = System::Drawing::Size(292, 273);
            this->Controls->Add(this->button1);
            this->Name = L"Form1";
            this->Text = L"Form1";
            this->Load += gcnew System::EventHandler(this, &Form1::Form1_Load);
            this->ResumeLayout(false);

        }
#pragma endregion
    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
        //code for sound production
        OnSound();
    }
    //NEW STUFF
    private: System::Void Form1_Load(System::Object^  sender, System::EventArgs^  e) {
                 Sound::init();
             }
    };
}

The contents of OnSound and sound.h and sound.cpp are from a sound example project that was created in Visual Studio 6.0 and modified for Visual Studio 2010 that works at playing the sound. My problem is that when I copy the code and try to use it in my own project, it compiles but doesn't play a sound when the button is pressed. Can anyone point me in the right direction?

user1846359
  • 233
  • 3
  • 12
  • You did check [all these](http://stackoverflow.com/search?q=%5Bc%2B%2B%5D%5Bwindows%5DPlaying+a+sound+in+C%2B%2B) I guess? – πάντα ῥεῖ Oct 06 '14 at 18:32
  • 1
    The C++11 standard does not know about sounds. You need an operating system specific library (or some other library abstracting above that, perhaps [libsdl](http://www.libsdl.org/)...) – Basile Starynkevitch Oct 06 '14 at 18:32
  • @BasileStarynkevitch : I don't think this is a C++11 question. It is definitely Windows (and their latest VC++ 2013 is only partially C++11 compliant). VC++ 2010 doesn't support much of C++11. OPs question suggests it compiles and runs but doesn't have the expected behavior of playing a sound. On a side note `mmsystem.h` is where Windows defines prototypes for basic functions of the Win32 sound system. – Michael Petch Oct 06 '14 at 18:41
  • Why not just use .net? [How to play a sound in C#, .NET](http://stackoverflow.com/questions/3502311/how-to-play-a-sound-in-c-net) – crashmstr Oct 06 '14 at 18:43
  • @crashmstr : not everyone uses managed languages for everything. Sometimes it is pointless and unnecessary overhead. – Michael Petch Oct 06 '14 at 18:45
  • @MichaelPetch true, but they made a decision to use C++/CLI for WinForms, so why not also look at .net for playing sounds? (in other words, they are already using .Net) – crashmstr Oct 06 '14 at 18:47
  • @crashmstr : Good point about the C++/CLI choice. Although I know years ago many people didn't want to learn C# so they chose C++CLI because it was more familiar. MS sort of put a kibosh on that since they dropped IDE support for Winforms in VS2012+ when using C++/CLI. MS doesn't intend for people to use C++/CLI in the future for anything more than shim interfaces. – Michael Petch Oct 06 '14 at 18:51
  • This may seem like a dumb question but have you verified the button press event handler is called? Either the sound code isn't working or the event handler isn't being called (and thus no sound) – Michael Petch Oct 06 '14 at 19:05
  • And probably the most obvious thing. In `Sound::init` you fill in a `WAVEFORMATEX` structure but never actually do anything with it which probably suggests the sound system isn't being initialized properly. – Michael Petch Oct 06 '14 at 19:10
  • @BasileStarynkevitch I've already included winmm.lib in the project. – user1846359 Oct 08 '14 at 18:47
  • @MichaelPetch Your WAVEFORMATEX structure idea sounds like a good lead. I'll look into it and report back. Thanks! – user1846359 Oct 08 '14 at 18:48
  • @πάνταῥεῖ I haven't looked through every single one, just the ones that look relevant. The weird part about this is that I took the sound.cpp and sound.h and the OnSound() function from a project that plays a sound when a button is pressed, but it doesn't work on my project. – user1846359 Oct 08 '14 at 18:51

2 Answers2

1

Your problem is scaling the floating point samples into 8-bit unsigned data. Since intensity is 0.5, samples is going to be between -0.5 and 0.5. Adding 128 and truncating to a byte will give you data values of 128 or 127 - which is effectively zero. To scale to the full range of the BYTE you need to multiply by 127.

BYTE* data=new BYTE[N];
for(int i=0;i<N;i++)
{
    data[i]=(BYTE)(signal[i]*127.f+128.f);
}

Additionally, unless you are okay with the quantization distortion, I would recommend adding some dither.

jaket
  • 9,140
  • 2
  • 25
  • 44
  • Thanks for your reply. I tried it and it still doesn't make a sound. I'm at a loss about what to try next. Any ideas? – user1846359 Oct 08 '14 at 18:46
0

Michael Petch was right about the incomplete initialization of the sound system.

I added:

int tt;
tt = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);

to Sound::init() and it worked!

Thanks to all those who read and/or replied.

user1846359
  • 233
  • 3
  • 12