I am writing a new framework for a game engine, but I'm stuck with one issue, OpenAL.
I'm usually using freealut for this, but i cant find it anywhere, the only site that hosted it is offline, and i don't have any copies of it. I even had to dissect some other guys' project to find openal32.lib. Either my google fu has grown weak, or the vast Internet really doesn't have any copies of it.
I found some example coding showing how to work openAL without the freealut framework, but i cant get it to load in multiple files, so i either have to find out why its not working, or somehow locate freealut, i found some sources for it in github, but at this moment, building freealut from source is out of the question.
I am using visual express c++ 2010 as the ide.
I modified the code I found, into this:
Basically its three commands to load, play and delete the sound files.
It works fine for one sound file, but when I try to load in more, it stops working.
#include "AudioLib.h"
#include <iostream>
#include <cstdlib>
#include <Windows.h>
#include <map>
#include <vector>
#include <AL\al.h>
#include <AL\alc.h>
using namespace std;
typedef map <const char *, ALuint > MapType;
MapType soundsbuffer;
MapType soundssource;
int endWithError(char* msg, int error=0)
{
//Display error message in console
cout << msg << "\n";
//system("PAUSE");
return error;
}
vector<const char *> soundslist;
ALCdevice *device;
ALCcontext *context;
int loadSound(const char * input) {
FILE *fp;
unsigned char* buf;
ALuint source;
ALuint buffer;
fp = NULL;
fp = fopen(input,"rb");
char type[4];
DWORD size,chunkSize;
short formatType,channels;
DWORD sampleRate, avgBytesPerSec;
short bytesPerSample, bitsPerSample;
DWORD dataSize;
//Check that the WAVE file is OK
fread(type,sizeof(char),4,fp); //Reads the first bytes in the file
if(type[0]!='R' || type[1]!='I' || type[2]!='F' || type[3]!='F') //Should be "RIFF"
return endWithError ("No RIFF"); //Not RIFF
fread(&size, sizeof(DWORD),1,fp); //Continue to read the file
fread(type, sizeof(char),4,fp); //Continue to read the file
if (type[0]!='W' || type[1]!='A' || type[2]!='V' || type[3]!='E') //This part should be "WAVE"
return endWithError("not WAVE"); //Not WAVE
fread(type,sizeof(char),4,fp); //Continue to read the file
if (type[0]!='f' || type[1]!='m' || type[2]!='t' || type[3]!=' ') //This part should be "fmt "
return endWithError("not fmt "); //Not fmt
//Now we know that the file is a acceptable WAVE file
//Info about the WAVE data is now read and stored
fread(&chunkSize,sizeof(DWORD),1,fp);
fread(&formatType,sizeof(short),1,fp);
fread(&channels,sizeof(short),1,fp);
fread(&sampleRate,sizeof(DWORD),1,fp);
fread(&avgBytesPerSec,sizeof(DWORD),1,fp);
fread(&bytesPerSample,sizeof(short),1,fp);
fread(&bitsPerSample,sizeof(short),1,fp);
fread(type,sizeof(char),4,fp);
if (type[0]!='d' || type[1]!='a' || type[2]!='t' || type[3]!='a') //This part should be "data"
return endWithError("Missing DATA"); //not data
fread(&dataSize,sizeof(DWORD),1,fp); //The size of the sound data is read
//Display the info about the WAVE file
cout << "Chunk Size: " << chunkSize << "\n";
cout << "Format Type: " << formatType << "\n";
cout << "Channels: " << channels << "\n";
cout << "Sample Rate: " << sampleRate << "\n";
cout << "Average Bytes Per Second: " << avgBytesPerSec << "\n";
cout << "Bytes Per Sample: " << bytesPerSample << "\n";
cout << "Bits Per Sample: " << bitsPerSample << "\n";
cout << "Data Size: " << dataSize << "\n";
buf= new unsigned char[dataSize]; //Allocate memory for the sound data
cout << fread(buf,sizeof(BYTE),dataSize,fp) << " bytes loaded\n"; //Read the sound data and display the
//number of bytes loaded.
//Should be the same as the Data Size if OK
//Now OpenAL needs to be initialized
//And an OpenAL Context
device = alcOpenDevice(NULL); //Open the device
if(!device) return endWithError("no sound device"); //Error during device oening
context = alcCreateContext(device, NULL); //Give the device a context
alcMakeContextCurrent(context); //Make the context the current
if(!context) return endWithError("no sound context"); //Error during context handeling
//Stores the sound data
ALuint frequency=sampleRate;; //The Sample Rate of the WAVE file
ALenum format=0; //The audio format (bits per sample, number of channels)
alGenBuffers(1, &buffer); //Generate one OpenAL Buffer and link to "buffer"
alGenSources(1, &source); //Generate one OpenAL Source and link to "source"
if(alGetError() != AL_NO_ERROR) return endWithError("Error GenSource"); //Error during buffer/source generation
//Figure out the format of the WAVE file
if(bitsPerSample == 8)
{
if(channels == 1)
format = AL_FORMAT_MONO8;
else if(channels == 2)
format = AL_FORMAT_STEREO8;
}
else if(bitsPerSample == 16)
{
if(channels == 1)
format = AL_FORMAT_MONO16;
else if(channels == 2)
format = AL_FORMAT_STEREO16;
}
if(!format) return endWithError("Wrong BitPerSample"); //Not valid format
alBufferData(buffer, format, buf, dataSize, frequency); //Store the sound data in the OpenAL Buffer
soundsbuffer[input] = buffer;
soundssource[input] = source;
soundslist.push_back(input);
if(alGetError() != AL_NO_ERROR) {
return endWithError("Error loading ALBuffer"); //Error during buffer loading
}
fclose(fp);
delete[] buf;
}
int playSound(const char * input) {
//Sound setting variables
ALfloat SourcePos[] = { 0.0, 0.0, 0.0 }; //Position of the source sound
ALfloat SourceVel[] = { 0.0, 0.0, 0.0 }; //Velocity of the source sound
ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 }; //Position of the listener
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 }; //Velocity of the listener
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 }; //Orientation of the listener
//First direction vector, then vector pointing up)
//Listener
alListenerfv(AL_POSITION, ListenerPos); //Set position of the listener
alListenerfv(AL_VELOCITY, ListenerVel); //Set velocity of the listener
alListenerfv(AL_ORIENTATION, ListenerOri); //Set orientation of the listener
ALuint source = soundssource[input];
ALuint buffer = soundsbuffer[input];
//Source
alSourcei (source, AL_BUFFER, buffer); //Link the buffer to the source
alSourcef (source, AL_PITCH, 1.0f ); //Set the pitch of the source
alSourcef (source, AL_GAIN, 1.0f ); //Set the gain of the source
alSourcefv(source, AL_POSITION, SourcePos); //Set the position of the source
alSourcefv(source, AL_VELOCITY, SourceVel); //Set the velocity of the source
alSourcei (source, AL_LOOPING, AL_FALSE ); //Set if source is looping sound
//PLAY
alSourcePlay(source); //Play the sound buffer linked to the source
if(alGetError() != AL_NO_ERROR) return endWithError("Error playing sound"); //Error when playing sound
//system("PAUSE"); //Pause to let the sound play
}
void deleteSound() {
//Clean-up
//Close the WAVE file
//Delete the sound data buffer
for(int i = 0; i < soundslist.size(); i++) {
const char * out = soundslist[i];
alDeleteSources(1, &soundssource[out]); //Delete the OpenAL Source
alDeleteBuffers(1, &soundsbuffer[out]);
}
//Delete the OpenAL Buffer
soundslist.clear();
alcMakeContextCurrent(NULL); //Make no context current
alcDestroyContext(context); //Destroy the OpenAL Context
alcCloseDevice(device);
}
So what I'm asking for:
I need either the freealut files, or some help with the code.
Any solutions?
Ok, the openal site seems to be partially back online.
For anyone who needs the link: http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx?RootFolder=http%3a%2f%2fconnect%2ecreativelabs%2ecom%2fopenal%2fDownloads%2fALUT&FolderCTID=0x01200073059C4C04B4D14B80686126F6C1A2E8
Related
I am trying to decode a video stream from the browser using the ffmpeg API. The stream is produced by the webcam and recorded with MediaRecorder as webm format. What I ultimately need is a vector of opencv cv::Mat objects for further processing.
I have written a C++ webserver using the uWebsocket library. The video stream is sent via websocket from the browser to the server once per second. On the server, I append the received data to my custom buffer and decode it with the ffmpeg API.
If I just save the data on the disk and later I play it with a media player, it works fine. So, whatever the browser sends is a valid video.
I do not think that I correctly understand how should the custom IO behave with network streaming as nothing seems to be working.
The custom buffer:
struct Buffer
{
std::vector<uint8_t> data;
int currentPos = 0;
};
The readAVBuffer method for custom IO
int MediaDecoder::readAVBuffer(void* opaque, uint8_t* buf, int buf_size)
{
MediaDecoder::Buffer* mbuf = (MediaDecoder::Buffer*)opaque;
int count = 0;
for(int i=0;i<buf_size;i++)
{
int index = i + mbuf->currentPos;
if(index >= (int)mbuf->data.size())
{
break;
}
count++;
buf[i] = mbuf->data.at(index);
}
if(count > 0) mbuf->currentPos+=count;
std::cout << "read : "<<count<<" "<<mbuf->currentPos<<", buff size:"<<mbuf->data.size() << std::endl;
if(count <= 0) return AVERROR(EAGAIN); //is this error that should be returned? It cannot be EOF since we're not done yet, most likely
return count;
}
The big decode method, that's supposed to return whatever frames it could read
std::vector<cv::Mat> MediaDecoder::decode(const char* data, size_t length)
{
std::vector<cv::Mat> frames;
//add data to the buffer
for(size_t i=0;i<length;i++) {
buf.data.push_back(data[i]);
}
//do not invoke the decoders until we have 1MB of data
if(((buf.data.size() - buf.currentPos) < 1*1024*1024) && !initializedCodecs) return frames;
std::cout << "decoding data length "<<length<<std::endl;
if(!initializedCodecs) //initialize ffmpeg objects. Custom I/O, format, decoder, etc.
{
//these are just members of the class
avioCtxPtr = std::unique_ptr<AVIOContext,avio_context_deleter>(
avio_alloc_context((uint8_t*)av_malloc(4096),4096,0,&buf,&readAVBuffer,nullptr,nullptr),
avio_context_deleter());
if(!avioCtxPtr)
{
std::cerr << "Could not create IO buffer" << std::endl;
return frames;
}
fmt_ctx = std::unique_ptr<AVFormatContext,avformat_context_deleter>(avformat_alloc_context(),
avformat_context_deleter());
fmt_ctx->pb = avioCtxPtr.get();
fmt_ctx->flags |= AVFMT_FLAG_CUSTOM_IO ;
//fmt_ctx->max_analyze_duration = 2 * AV_TIME_BASE; // read 2 seconds of data
{
AVFormatContext *fmtCtxRaw = fmt_ctx.get();
if (avformat_open_input(&fmtCtxRaw, "", nullptr, nullptr) < 0) {
std::cerr << "Could not open movie" << std::endl;
return frames;
}
}
if (avformat_find_stream_info(fmt_ctx.get(), nullptr) < 0) {
std::cerr << "Could not find stream information" << std::endl;
return frames;
}
if((video_stream_idx = av_find_best_stream(fmt_ctx.get(), AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0)) < 0)
{
std::cerr << "Could not find video stream" << std::endl;
return frames;
}
AVStream *video_stream = fmt_ctx->streams[video_stream_idx];
AVCodec *dec = avcodec_find_decoder(video_stream->codecpar->codec_id);
video_dec_ctx = std::unique_ptr<AVCodecContext,avcodec_context_deleter> (avcodec_alloc_context3(dec),
avcodec_context_deleter());
if (!video_dec_ctx)
{
std::cerr << "Failed to allocate the video codec context" << std::endl;
return frames;
}
avcodec_parameters_to_context(video_dec_ctx.get(),video_stream->codecpar);
video_dec_ctx->thread_count = 1;
/* video_dec_ctx->max_b_frames = 0;
video_dec_ctx->frame_skip_threshold = 10;*/
AVDictionary *opts = nullptr;
av_dict_set(&opts, "refcounted_frames", "1", 0);
av_dict_set(&opts, "deadline", "1", 0);
av_dict_set(&opts, "auto-alt-ref", "0", 0);
av_dict_set(&opts, "lag-in-frames", "1", 0);
av_dict_set(&opts, "rc_lookahead", "1", 0);
av_dict_set(&opts, "drop_frame", "1", 0);
av_dict_set(&opts, "error-resilient", "1", 0);
int width = video_dec_ctx->width;
videoHeight = video_dec_ctx->height;
if(avcodec_open2(video_dec_ctx.get(), dec, &opts) < 0)
{
std::cerr << "Failed to open the video codec context" << std::endl;
return frames;
}
AVPixelFormat pFormat = AV_PIX_FMT_BGR24;
img_convert_ctx = std::unique_ptr<SwsContext,swscontext_deleter>(sws_getContext(width, videoHeight,
video_dec_ctx->pix_fmt, width, videoHeight, pFormat,
SWS_BICUBIC, nullptr, nullptr,nullptr),swscontext_deleter());
frame = std::unique_ptr<AVFrame,avframe_deleter>(av_frame_alloc(),avframe_deleter());
frameRGB = std::unique_ptr<AVFrame,avframe_deleter>(av_frame_alloc(),avframe_deleter());
int numBytes = av_image_get_buffer_size(pFormat, width, videoHeight,32 /*https://stackoverflow.com/questions/35678041/what-is-linesize-alignment-meaning*/);
std::unique_ptr<uint8_t,avbuffer_deleter> imageBuffer((uint8_t *) av_malloc(numBytes*sizeof(uint8_t)),avbuffer_deleter());
av_image_fill_arrays(frameRGB->data,frameRGB->linesize,imageBuffer.get(),pFormat,width,videoHeight,32);
frameRGB->width = width;
frameRGB->height = videoHeight;
initializedCodecs = true;
}
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = nullptr;
pkt.size = 0;
int read_frame_return = 0;
while ( (read_frame_return=av_read_frame(fmt_ctx.get(), &pkt)) >= 0)
{
readFrame(&frames,&pkt,video_dec_ctx.get(),frame.get(),img_convert_ctx.get(),
videoHeight,frameRGB.get());
//if(cancelled) break;
}
avioCtxPtr->eof_reached = 0;
avioCtxPtr->error = 0;
//flush
// readFrame(frames.get(),nullptr,video_dec_ctx.get(),frame.get(),
// img_convert_ctx.get(),videoHeight,frameRGB.get());
avioCtxPtr->eof_reached = 0;
avioCtxPtr->error = 0;
if(frames->size() <= 0)
{
std::cout << "buffer pos: "<<buf.currentPos<<", buff size:"<<buf.data.size()
<<",read_frame_return:"<<read_frame_return<< std::endl;
}
return frames;
}
What I would expect to happen would be for a continuous extraction of cv::Mat frames as I feed it more and more data. What actually happens is that after the the buffer is fully read I see:
[matroska,webm # 0x507b450] Read error at pos. 1278266 (0x13813a)
[matroska,webm # 0x507b450] Seek to desired resync point failed. Seeking to earliest point available instead.
And then no more bytes are read from the buffer even if later I increase the size of it.
There is something terribly wrong I'm doing here and I don't understand what.
What I ended up doing was to do the reading of the incoming data and actual decoding in a different thread. The read method, however, will just block if there are no more bytes available, waiting until anything is coming.
When new bytes are arriving, they're added to the buffer and the conditional_variable signals the waiting thread to wake up and start reading data again from the buffer.
It works well enough.
I'm playing around with some code in a project; specifically a function that takes SVG images and makes pngs out of them.
I have this:
typedef std::vector<uint8_t> BinaryBuffer;
BinaryBuffer readFile(fs::path const& path) {
BinaryBuffer ret;
fs::ifstream f(path, std::ios::binary);
f.seekg(0, std::ios::end);
ret.resize(f.tellg());
f.seekg(0);
f.read(reinterpret_cast<char*>(ret.data()), ret.size());
if (!f) throw std::runtime_error("File cannot be read: " + path.string());
return ret;
}
void loadSVG(Bitmap& bitmap, fs::path const& filename) {
double factor = config["graphic/svg_lod"].f();
// Try to load a cached PNG instead
if (cache::loadSVG(bitmap, filename, factor)) return;
std::clog << "image/debug: Loading SVG: " + filename.string() << std::endl;
// Open the SVG file in librsvg
#if !GLIB_CHECK_VERSION(2, 36, 0) // Avoid deprecation warnings
g_type_init();
#endif
GError* pError = nullptr;
std::shared_ptr<RsvgHandle> svgHandle(rsvg_handle_new_with_flags(RSVG_HANDLE_FLAG_KEEP_IMAGE_DATA), g_object_unref);
rsvg_handle_set_base_uri(svgHandle.get(),filename.string().c_str());
BinaryBuffer data = readFile(filename);
std::clog << "svg/debug: svg data size is: " << data.size() << std::endl;
gboolean result = rsvg_handle_write(svgHandle.get(), data.data(), data.size(), &pError);
// rsvg_handle_new_from_file(filename.string().c_str(), &pError)
if (result != TRUE) {
g_error_free(pError);
throw std::runtime_error("Unable to load " + filename.string());
}
else {
std::clog << "svg/debug: SVG loaded succesfully." << std::endl;
}
// Get SVG dimensions
RsvgDimensionData svgDimension;
rsvg_handle_get_dimensions(svgHandle.get(), &svgDimension);
// Prepare the pixel buffer
std::clog << "svg/debug: svg width is: " << svgDimension.width << ", and height: " << svgDimension.height << std::endl;
bitmap.resize(svgDimension.width*factor, svgDimension.height*factor);
bitmap.fmt = pix::INT_ARGB;
bitmap.linearPremul = true;
// Raster with Cairo
std::shared_ptr<cairo_surface_t> surface(
cairo_image_surface_create_for_data(&bitmap.buf[0], CAIRO_FORMAT_ARGB32, bitmap.width, bitmap.height, bitmap.width * 4),
cairo_surface_destroy);
std::shared_ptr<cairo_t> dc(cairo_create(surface.get()), cairo_destroy);
cairo_scale(dc.get(), factor, factor);
gboolean renderRes = TRUE;
renderRes = rsvg_handle_render_cairo(svgHandle.get(), dc.get());
if (renderRes != TRUE) {
throw std::runtime_error("Unable to render " + filename.string());
}
// Change byte order from BGRA to RGBA
for (uint32_t *ptr = reinterpret_cast<uint32_t*>(&*bitmap.buf.begin()), *end = ptr + bitmap.buf.size() / 4; ptr < end; ++ptr) {
uint8_t* pixel = reinterpret_cast<uint8_t*>(ptr);
uint8_t r = pixel[2], g = pixel[1], b = pixel[0], a = pixel[3];
pixel[0] = r; pixel[1] = g; pixel[2] = b; pixel[3] = a;
}
bitmap.fmt = pix::CHAR_RGBA;
// Write to cache so that it can be loaded faster the next time
fs::path cache_filename = cache::constructSVGCacheFileName(filename, factor);
fs::create_directories(cache_filename.parent_path());
writePNG(cache_filename, bitmap);
}
But it fails in rsvg_handle_render_cairo... I don't know why. A previous version of the function using rsvg_handle_new_from_file (which doesn't use the BinaryBuffer struct) worked fine. Note, however that essentially the same struct and readFile function is used elsewhere without any issues. And from the debug messages I've dropped in there I can see that the file is indeed getting read. I am also getting correct dimensions from my svg file and there don't seem to be any errors prior to the call to the render function (so I'd assume it's parsed ok) but maybe not?
The answer was ridiculously simple. I was missing rsvg_handle_close(svgHandle.get(), &pError);
I'm trying to create a C++ program with Waveform Audio library that would be playing AudioFrames (raw audio data, each frame consists of about 1920 bytes) provided by another program (right now I'm just simulating that by reading file as AudioFrames). Modifying code from this thread I was able to make SoundPlayer class that does the job, but the output I get is extremely choppy. It's gets better with bigger frame sizes, but even with frames as big as 96000 bytes the audio still glitches every second or so (and I need the frames too be much smaller than that).
How can I fix this issue?
Here is the test file I'm using. And here is the code itself:
#include <windows.h>
#include <iostream>
#pragma comment(lib, "Winmm.lib")
constexpr int FRAME_SIZE_IN_BYTES = 1920;
struct AudioFrame
{
char *Data;
int DataSize;
};
class SoundPlayer
{
public:
SoundPlayer()
{
// 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 = 16; // Bits per sample per channel
m_waveFormat.nSamplesPerSec = 48000; // 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;
}
void Play(AudioFrame* af)
{
// 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)
{
std::cout << "Sound card cannot be opened." << std::endl;
return;
}
// Create the wave header for our sound buffer
m_waveHeader.lpData = af->Data;
m_waveHeader.dwBufferLength = af->DataSize;
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)
{
std::cout << "Error preparing Header!" << std::endl;
return;
}
ResetEvent(m_done); // Reset our Event so it is non-signaled, it will be signaled again with buffer finished
// Play the sound!
if (waveOutWrite(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error writing to sound card!" << std::endl;
return;
}
// Wait until sound finishes playing
if (WaitForSingleObject(m_done, INFINITE) != WAIT_OBJECT_0)
{
std::cout << "Error waiting for sound to finish" << std::endl;
return;
}
// Unprepare our wav header
if (waveOutUnprepareHeader(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error unpreparing header!" << std::endl;
return;
}
// Close the wav device
if (waveOutClose(m_waveOut) != MMSYSERR_NOERROR)
{
std::cout << "Sound card cannot be closed!" << std::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
};
int main()
{
FILE * fileDes;
fopen_s(&fileDes, "Ducksauce.raw", "rb");
if (fileDes == nullptr)
std::cout << "File opening failed.\n";
int bufferSize = FRAME_SIZE_IN_BYTES;
char *buffer = new char[bufferSize];
SoundPlayer sp;
while (fread(buffer, sizeof(char), bufferSize, fileDes) > 0)
{
AudioFrame af;
af.Data = buffer;
af.DataSize = bufferSize;
sp.Play(&af);
}
fclose(fileDes);
delete[] buffer;
return 0;
}
Edit: Version number 2. Still doesn't work as intended.
#include <windows.h>
#include <iostream>
#pragma comment(lib, "Winmm.lib")
constexpr int FRAME_SIZE_IN_BYTES = 1920;
struct AudioFrame
{
char *Data;
int DataSize;
};
class SoundPlayer
{
public:
SoundPlayer()
{
// 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 = 16; // Bits per sample per channel
m_waveFormat.nSamplesPerSec = 48000; // 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;
// 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)
{
std::cout << "Sound card cannot be opened." << std::endl;
return;
}
}
~SoundPlayer()
{
// Close the wav device
if (waveOutClose(m_waveOut) != MMSYSERR_NOERROR)
{
std::cout << "Sound card cannot be closed!" << std::endl;
return;
}
// Release our event handle
CloseHandle(m_done);
}
void StartPlaying(AudioFrame* af)
{
// Create the wave header for our sound buffer
m_waveHeader.lpData = af->Data;
m_waveHeader.dwBufferLength = af->DataSize;
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)
{
std::cout << "Error preparing Header!" << std::endl;
return;
}
ResetEvent(m_done); // Reset our Event so it is non-signaled, it will be signaled again with buffer finished
// Play the sound!
if (waveOutWrite(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error writing to sound card!" << std::endl;
return;
}
}
void WaitUntilFrameFinishes()
{
// Wait until sound finishes playing
if (WaitForSingleObject(m_done, INFINITE) != WAIT_OBJECT_0)
{
std::cout << "Error waiting for sound to finish" << std::endl;
return;
}
// Unprepare our wav header
if (waveOutUnprepareHeader(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error unpreparing header!" << std::endl;
return;
}
}
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
};
int main()
{
FILE * fileDes;
fopen_s(&fileDes, "Ducksauce.raw", "rb");
if (fileDes == nullptr)
std::cout << "File opening failed.\n";
int bufferSize = FRAME_SIZE_IN_BYTES;
char *buffer = new char[bufferSize];
SoundPlayer sp;
// Read first time
fread(buffer, sizeof(char), bufferSize, fileDes);
while (true)
{
AudioFrame af;
af.Data = buffer;
af.DataSize = bufferSize;
// Start playing, but don't block
sp.StartPlaying(&af);
// Prepare the next chunk
if (fread(buffer, sizeof(char), bufferSize, fileDes) <= 0)
break;
// Now block the code, waiting with next chunk already loaded
// and ready to be played in the next iteration.
sp.WaitUntilFrameFinishes();
}
fclose(fileDes);
delete[] buffer;
return 0;
}
Edit 2: It works if I add this before while:
for (int i = 0; i < 3; i++ )
{
fread(buffer, sizeof(char), bufferSize, fileDes);
af.Data = buffer;
af.DataSize = bufferSize;
sp.StartPlaying(&af);
}
Also I modified while a bit too:
while (true)
{
// Prepare the next chunk
if (fread(buffer, sizeof(char), bufferSize, fileDes) <= 0)
break;
// Now block the code, waiting with next chunk already loaded
// and ready to be played in the next iteration.
sp.WaitUntilFrameFinishes();
af.Data = buffer;
af.DataSize = bufferSize;
sp.StartPlaying(&af);
}
You should read the data from disk while the sound plays, not in between buffers!
If you can't read the whole file at once, you should change your Play function so that it doesn't just call WaitForSingleObject. Using it makes your code block and wait until the sound stops playing.
What you need instead is to start playing, then go back to your reading loop, prepare the next buffer, and then wait for the music to end, like so (in SoundPlayer):
void WaitUntilFrameFinishes() {
// Wait until sound finishes playing
if (WaitForSingleObject(m_done, INFINITE) != WAIT_OBJECT_0)
// ... move all the code from Play till the end here
}
Then back in the main loop:
// Read first frame
fread(buffer, sizeof(char), bufferSize, fileDes);
while (true)
{
AudioFrame af;
af.Data = buffer;
af.DataSize = bufferSize;
// Start playing, but don't block
sp.Play(&af);
// Prepare the next chunk
if (fread(buffer, sizeof(char), bufferSize, fileDes) <= 0) {
break;
// Now block the code, waiting with next chunk already loaded
// and ready to be played in the next iteration.
sp.WaitUntilFrameFinishes();
}
Ideally you'd also wrap the fread calls into something that can provide chunks in a nicer way.
After day of trying to figure out how to go about audio playback based on the documentation alone I found this excellent tutorial. If anyone found this thread while trying to create audio playback using Waveform audio it's a very good point of reference (surely much better than my buggy code above).
About my code I suspect it doesn't work correctly because one is supposed to keep the AudioFrames queue using waveOutWrite() with at least several frames at all time to prevent situation where sound card would have to wait for another AudioFrame.
I am using the libjpeg library to read and copy a jpeg into an editing program that I am writing in C++
I have a display buffer which is a vector of a datatype called ColorData
All ColorData consists of is 3 floats (RGB)
Here is my code that opens the jpeg files
PixelBuffer * IOManager::load_jpg_to_pixel_buffer(const char *file_name){
struct jpeg_decompress_struct cinfo;
FILE * infile;
JSAMPARRAY buffer;
if ((infile = fopen(file_name, "rb")) == NULL) {
std::cout << "Could not open the jpg file: " << file_name << std::endl;
return nullptr;
}
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, infile);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
int width = static_cast<int>(cinfo.output_width);
int height = static_cast<int>(cinfo.output_height);
std::cout << typeid(cinfo.colormap).name() << std::endl;
std::cout << "Width: " << width << "Height: " << height << std::endl;
PixelBuffer * image_buffer = new PixelBuffer(width, height, ColorData());
std::cout << cinfo.output_components << std::endl;
buffer = (*cinfo.mem->alloc_sarray)
((j_common_ptr) &cinfo, JPOOL_IMAGE, cinfo.output_width * cinfo.output_components, 1);
/* Step 6: while (scan lines remain to be read) */
/* jpeg_read_scanlines(...); */
/* Here we use the library's state variable cinfo.output_scanline as the
* loop counter, so that we don't have to keep track ourselves.
*/
while (cinfo.output_scanline < cinfo.output_height) {
/* jpeg_read_scanlines expects an array of pointers to scanlines.
* Here the array is only one element long, but you could ask for
* more than one scanline at a time if that's more convenient.
*/
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
/* Assume put_scanline_someplace wants a pointer and sample count. */
}
return nullptr;
}
How can I get the RGB value from the jpeg using the libjpeg?
The RGB values are in buffer. It's actually an array of arrays, so you have to index buffer[0].
Something like this:
while (cinfo.output_scanline < cinfo.output_height)
{
(void) jpeg_read_scanlines(&cinfo, buffer, 1);
// get the pointer to the row:
unsigned char* pixel_row = (unsigned char*)(buffer[0]);
// iterate over the pixels:
for(int i = 0; i < cinfo.output_width; i++)
{
// convert the RGB values to a float in the range 0 - 1
float red = (float)(*pixel_row++) / 255.0f;
float green = (float)(*pixel_row++) / 255.0f;
float blue = (float)(*pixel_row++) / 255.0f;
}
}
This is assuming cinfo.output_components is 3.
I have created a small sample program in C++ trying to play sounds with OpenAL Soft. The program crashes when compiled in release mode, when compiled in debug mode it works. I'm on OS X using 1.17.2.
I get this error message:
SoundTest(28958,0x70000021d000) malloc: *** error for object 0x7fbdd26062c8: incorrect checksum for freed object - object was probably modified after being freed.
This is the full sample program:
#include <iostream>
#include <AL/alc.h>
#include <AL/al.h>
#include <cmath>
using namespace std;
int main() {
// Reset error state just to be sure
alGetError();
ALCdevice *device = alcOpenDevice(NULL);
if (device == NULL) {
cout << "Error creating device" << endl;
return 1;
}
ALCcontext *context = alcCreateContext(device, NULL);
if (!alcMakeContextCurrent(context)) {
cout << "Failed to make context current" << endl;
return 1;
}
ALuint buffer;
// Set up sound buffer
alGenBuffers((ALuint)1, &buffer);
// Fill buffer with sine-wave
float freq = 440.f;
int seconds = 4;
unsigned sample_rate = 22050;
size_t buf_size = seconds * sample_rate;
short *samples;
samples = new short[buf_size];
for(int i=0; i<buf_size; ++i) {
samples[i] = (short) (32760 * sin((2.f * float(M_PI) * freq) / sample_rate * i ));
}
alBufferData(buffer, AL_FORMAT_MONO16, samples, buf_size, sample_rate);
ALuint source;
// Set up sound source
alGenSources((ALuint)1, &source);
alSourcef(source, AL_PITCH, 1);
alSourcef(source, AL_GAIN, 1);
alSource3f(source, AL_POSITION, 0, 0, 0);
alSource3f(source, AL_VELOCITY, 0, 0, 0);
alSourcei(source, AL_LOOPING, AL_FALSE);
alSourcei(source, AL_BUFFER, buffer);
// Start playing
alSourcePlay(source);
// Wait until the sound stops playing
ALenum state;
do {
alGetSourcei(source, AL_SOURCE_STATE, &state);
} while (state == AL_PLAYING);
// Clean up
alDeleteSources(1, &source);
alcMakeContextCurrent(NULL);
alcDestroyContext(context);
alcCloseDevice(device);
return 0;
}
If anyone else is having the same problem, have a look at this. The issue is resolved now.