Sign in to follow this  
Palidor

OpenAL Pseudo-Crash: alBufferData

Recommended Posts

Hi all, Hopefully someone can help shed some light on the issue I am having here. I'm currently writing a .wav loader function for OpenAL, as alut is deprecated, and I would rather code it myself anyway. I found a code sample online that finished to hold the header information for a .wav, as listed here: <code> typedef struct { uint32_t Chunk_ID; uint32_t Chunk_Size; uint32_t Format; uint32_t Sub_Chunk1_ID; uint32_t Sub_Chunk1_Size; uint16_t Audio_Format; uint16_t Num_Channels; uint32_t Sample_Rate; uint32_t Byte_Rate; uint16_t Block_Align; uint16_t Bits_Per_Sample; uint32_t Sub_Chunk2_ID; uint32_t Sub_Chunk2_Size; } WAV_header_t; </code> I then began coding the following function (please keep in mind that this is not final, I just want to prove that it works before I refactor how it is setup): <code> void CSoundManager::LoadWAVFile(const char* _szFileName) { fstream fin(_szFileName, std::ios_base::in | std::ios_base::binary); if(fin.is_open()) { WAV_header_t header; ALuint buffer; ALenum format; ALvoid* data; ALsizei size; ALsizei freq; printf("Sound Manager: File opened!\n"); // Read the header fin.read((char*)&header, sizeof(WAV_header_t)); // Print out the header information to the console PrintWAVHeaderInfo(header); // Allocate enough memory to hold the .wav data data = (ALvoid*)malloc(header.Sub_Chunk2_Size); // Read the data into the allocated ALvoid* fin.read((char*)&data, sizeof(header.Sub_Chunk2_Size)); // Close the file, as we are done reading all the information fin.close(); // Hard code the bufferID to the m_unBuffers[0] for now // This has been tested to be a valid BufferID buffer = m_unBuffers[0]; // Custom function that uses the number of channels and bits per sample to choose the correct ALenum // FULLY TESTED format = GetSoundFormat(header.Num_Channels, header.Bits_Per_Sample); // Set the size equal to the size of the data size = header.Sub_Chunk2_Size; // Set the frequency equal to the sample rate freq = header.Sample_Rate; // Bind the data to the buffer alBufferData(m_unBuffers[0], format, data, size, freq); // Anything after is not including, as it doesn't get past the previous line } else { printf("Sound Manager: File did not open!\n"); return; } } </code> The GetSoundFormat function is coded as follows, and it chooses the correct enum as well: <code> ALenum GetSoundFormat(uint16_t _numChannels, uint16_t _bitsPerSample) { if(_numChannels == 1) { if(_bitsPerSample == 8) return AL_FORMAT_MONO8; else return AL_FORMAT_MONO16; } else { if(_bitsPerSample == 8) return AL_FORMAT_STEREO8; else return AL_FORMAT_STEREO16; } } </code> I checked the header to ensure it is being loaded in correctly, and the following information is printed to the log: <code> Sound Manager: File opened! Chunk ID: 1179011410 Chunk Size: 4678 Format: 1163280727 Sub Chunk1 ID: 544501094 Sub Chunk1 Size: 16 Audio Format: 1 Num Channels: 1 Sample Rate: 44100 Byte Rate: 88200 Block Align: 2 Bits Per Sample: 16 Sub Chunk2 ID: 1635017060 Sub Chunk2 Size: 4590 </code> The following is the next chunk of information spit out to the Log, at which point the program stops working: <code> Loading program into debugger… GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008) Copyright 2004 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-apple-darwin".warning: Unable to read symbols for "/System/Library/Frameworks/UIKit.framework/UIKit" (file not found). warning: Unable to read symbols from "UIKit" (not yet mapped into memory). warning: Unable to read symbols for "/System/Library/Frameworks/OpenGLES.framework/OpenGLES" (file not found). warning: Unable to read symbols from "OpenGLES" (not yet mapped into memory). warning: Unable to read symbols for "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" (file not found). warning: Unable to read symbols from "CoreGraphics" (not yet mapped into memory). Program loaded. sharedlibrary apply-load-rules all Attaching to program: `/Users/trptultima/Library/Application Support/iPhone Simulator/User/Applications/C6089826-EB29-4E9C-9C8A-6CAC423C218C/OGLGame.app/OGLGame', process 2557. Re-enabling shared library breakpoint 1 Re-enabling shared library breakpoint 2 Re-enabling shared library breakpoint 3 </code> I am also developing on a macintosh, working in the iPhone simulator, if that helps. Thank you in advance to anyone that can help! :-) - Sean

Share this post


Link to post
Share on other sites
No warranty of fitness, you may need to change things to fit what you need to do, and about all I'll promise is that this generally seems to work in my applications.

The bit labeled Important may be relevant to your problem.

OpenAL: a wonderful idea with difficult, frustrating mechanics; I have gone through misery trying to get multichannel speaker systems to behave properly with it...



//free for any use, as far as I know. Hastily fudged up from some other stuff I found once, which was in even uglier shape. No warranty expressed or implied.

enum WAVEFILETYPE
{
WF_unknown = 0,
WF_EX = 1,
WF_EXT = 2
};


class WAVEFILEDATA //was INFO
{
public:
WAVEFILETYPE wfType;
WAVEFORMATEXTENSIBLE wfEXT; // For non-WAVEFORMATEXTENSIBLE wavefiles, the header is stored in the Format member of wfEXT
unsigned long ulDataSize;
unsigned long ulDataOffset;
unsigned long ulFormat;
FILE *pFile;

inline WAVEFILEDATA()
{
pFile = NULL;
wfType = WF_unknown;
ulFormat = 0;
ulDataSize = 0;
ulDataOffset = 0;
}

~WAVEFILEDATA()
{
if (pFile)
fclose(pFile);
}

enum WFResult {PW_OK, PW_NoFile, PW_NotWave, PF_NotMono, PW_NotValidWave};

/* I don't understand this code. It seems to read the whole file, and it apparently believes
* the size of the LAST data chunk it finds... is this just stupid?
*/
//if all goes well, return true, with file cursor positioned at data and *this filled in
//WE ONLY ACCEPT mono8 and mono16!
WFResult parse(const char* n)
{
#pragma pack(push, 4)

struct WAVEFILEHEADER
{
char szRIFF[4];
unsigned long ulRIFFSize;
char szWAVE[4];
} ;

struct RIFFCHUNK
{
char szChunkName[4];
unsigned long ulChunkSize;
};

struct WAVEFMT
{
unsigned short usFormatTag;
unsigned short usChannels;
unsigned long ulSamplesPerSec;
unsigned long ulAvgBytesPerSec;
unsigned short usBlockAlign;
unsigned short usBitsPerSample;
unsigned short usSize;
unsigned short usReserved;
unsigned long ulChannelMask;
GUID guidSubFormat;
};

#pragma pack(pop)
pFile = fopen(n, "rb");
if (!pFile)
return PW_NoFile;

// Read Wave file header
WAVEFILEHEADER waveFileHeader;
fread(&waveFileHeader, 1, sizeof(WAVEFILEHEADER), pFile);
if (_strnicmp(waveFileHeader.szRIFF, "RIFF", 4) ||
_strnicmp(waveFileHeader.szWAVE, "WAVE", 4))
return PW_NotWave;

RIFFCHUNK riffChunk;
while (fread(&riffChunk, 1, sizeof(RIFFCHUNK), pFile) == sizeof(RIFFCHUNK))
{
if (_strnicmp(riffChunk.szChunkName, "fmt ", 4) == 0)
{
if (riffChunk.ulChunkSize <= sizeof(WAVEFMT))
{
WAVEFMT waveFmt;
fread(&waveFmt, 1, riffChunk.ulChunkSize, pFile);

// Determine if this is a WAVEFORMATEX or WAVEFORMATEXTENSIBLE wave file
if (waveFmt.usFormatTag == WAVE_FORMAT_PCM)
{
wfType = WF_EX;
memcpy(&wfEXT.Format, &waveFmt, sizeof(PCMWAVEFORMAT));
}
else if (waveFmt.usFormatTag == WAVE_FORMAT_EXTENSIBLE)
{
wfType = WF_EXT;
memcpy(&wfEXT, &waveFmt, sizeof(WAVEFORMATEXTENSIBLE));
}
}
else
{
fseek(pFile, riffChunk.ulChunkSize, SEEK_CUR);
}
}
else if (_strnicmp(riffChunk.szChunkName, "data", 4) == 0)
{
ulDataSize = riffChunk.ulChunkSize;
ulDataOffset = ftell(pFile);
fseek(pFile, riffChunk.ulChunkSize, SEEK_CUR);
}
else //not format, not data
{
fseek(pFile, riffChunk.ulChunkSize, SEEK_CUR);
}

//we know enough to stop scanning
if (wfType != WF_unknown && ulDataSize != 0)
break;

// Ensure that we are correctly aligned for next chunk
if (riffChunk.ulChunkSize & 1)
fseek(pFile, 1, SEEK_CUR);
} //get next chunk

if (ulDataSize == 0 || ulDataOffset == 0)
return PW_NotValidWave;
if (wfType != WF_EX && wfType != WF_EXT)
return PW_NotValidWave;
if (wfType == WF_EX && wfEXT.Format.nChannels == 1)
return PW_OK;
if (wfType == WF_EXT && (wfEXT.Format.nChannels == 1) && (wfEXT.dwChannelMask == SPEAKER_FRONT_CENTER))
return PW_OK;
return PF_NotMono;
}

WFResult ready();
};

/* This class sucks a mono8 or mono16 wav file into one or more AL buffers
*/
class WaveFile {
vector<ALuint> bufferIds;
public:
char* name;
float secsPerBuffer;

inline WaveFile()
{
name = NULL;
secsPerBuffer = 0;
}

bool open(const char* n)
{
clean();
char buf[MAX_PATH];
name = _strdup(n);
sprintf(buf, "sounds\\%s", n);
WAVEFILEDATA wfi;
switch (wfi.parse(buf))
{
case WAVEFILEDATA::PW_OK: break;
default:
return false;
}
switch (wfi.ready())
{
case WAVEFILEDATA::PW_OK: break;
default:
return false;
}
//parse left us positioned at the data...

#if 01 //users buffers about about 1 sec each
unsigned int blockAlign = wfi.wfEXT.Format.nBlockAlign;
if (blockAlign == 0)
return false;
unsigned int avgBytesPerSec = wfi.wfEXT.Format.nAvgBytesPerSec;

unsigned int bufferSize = avgBytesPerSec / 1; //1 seconds worth

// IMPORTANT : The Buffer Size must be an exact multiple of the BlockAlignment ...
bufferSize -= (bufferSize % blockAlign);
#else //one buffer, whole file
unsigned int bufferSize = wfi.ulDataSize;
#endif
char* buffer = (char*)malloc(bufferSize);
if (buffer == NULL)
return false;
secsPerBuffer = (float)bufferSize / avgBytesPerSec;
do {
unsigned long ulOffset = ftell(wfi.pFile);

if ((ulOffset - wfi.ulDataOffset + bufferSize) > wfi.ulDataSize)
bufferSize = wfi.ulDataSize - (ulOffset - wfi.ulDataOffset);
if (bufferSize == 0)
break; //done

unsigned long size = (unsigned long)fread(buffer, 1, bufferSize, wfi.pFile);

ALuint bufferId;
alGenBuffers(1, &bufferId);
alBufferData(bufferId, wfi.ulFormat, buffer, size, wfi.wfEXT.Format.nSamplesPerSec);
bufferIds.push_back(bufferId);
} while (true);
free(buffer);
return true;
}

inline unsigned int bufferCount() const
{
return (unsigned int)bufferIds.size();
}

inline ALuint getBuffer(const unsigned int i)
{
if (i >= bufferCount())
return 0;
return bufferIds[i];
}

void clean()
{
free(name);
name = NULL;
while (bufferCount() != 0)
{
alDeleteBuffers(1, &bufferIds.back());
bufferIds.pop_back();
}
}

inline ~WaveFile()
{
clean();
}
};

Share this post


Link to post
Share on other sites
Hey Scott,

Thanks for the code, however i'm not sure how to use it, as i've very new to audio coding in general. I was hoping i could figure out where my code has gone wrong, and i'm comparing mine to yours, but I i'm having a hard time figuring it out since the one you have seems to have a lot more special cases.

Does anyone have any clue why my code doesn't work as it? :-\

Share this post


Link to post
Share on other sites
Quote:
Original post by Palidor
Hey Scott,

Thanks for the code, however i'm not sure how to use it, as i've very new to audio coding in general. I was hoping i could figure out where my code has gone wrong, and i'm comparing mine to yours, but I i'm having a hard time figuring it out since the one you have seems to have a lot more special cases.

Does anyone have any clue why my code doesn't work as it? :-\


Focus on the part of my code that says IMPORTANT: and talks about buffers being a whole multiple of samples. I don't know if that's your issue, but I do know things messed up when it wasn't there, and it doesn't look like your code was accounting for the issue.

Most of the special cases in my junk are due to the, um, richness of the data formats that live inside .wav files (and I don't think my code covers all of them). It's possible you can strip a lot of that.

Share this post


Link to post
Share on other sites
Hey Scott,

Thanks again for replying! I looked at the chunk of code that was labeled important, and i noticed that it was only loading 1 second worth of the audio. I assume this due to streaming. I was checking the values output based on the header i read in, and my comparable nAvgBytesPerSec would be the Byte Rate variable in my structure. The the value for this was 88200 and the block align value for me is 2. According to the note you had, this would be fine since it's a multiple of 2. I wish i knew what was wrongf... I'm beginning to wish i never started working with OpenAL lol

-Sean

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this