Audio fix for Windows
by Vern Jensen · in Torque Game Builder · 06/17/2009 (2:01 pm) · 17 replies
Okay, so I've had problems with "clicks" or "pops" in my sounds when I run the game on Windows using TGB 1.7.4, but no problems on Mac, and after trying lots of things (.ogg versus .wav, and some other things), finally resorted to trying to upgrade OpenAL 1.1 in the engine. It seemed to fix the problem. My steps for how I did this are below in case they are helpful to others.
But I have a question. The final step in upgrading was to remove "OpenAL32.dll" from the game's folder, since OpenAL 1.1 has it as a library built-in to the project. But: I also discovered that by removing this file from *previous* versions of my game (i.e. ones with the older version of OpenAL), this ALSO fixes the sound problems I was having... *without* upgrading to OpenAL. So my questions are: 1) What does a ".dll" file do? (I'm a Mac guy, illiterate to Windows.) 2) By removing this file, was the game simply defaulting to some installed copy of OpenAL on Windows (that I'm assuming was newer). 3) How can I know if I "upgraded" OpenAL in TGB correctly, since removing this the .dll seems to be what "fixed" it, versus the changes I made to the engine?
In any case, here are the steps I followed to upgrade to OpenAL. After doing these steps, if you do not remove OpenAL32.dll from the game's folder before running, the game will crash, which leads me to believe I probably did the upgrade correctly. (i.e. the OpenAL library is now compiled into the TGB app instead of shipping separately as a .dll file.) But this is an assumption, since I don't really know what a .dll file is.
-------------
UPGRADING TGB to OpenAL 1.1
DISCLAIMER: Step 4 gets the engine to compile, but might eliminate some audio functionality used for different "environments." However, I've never seen documented anywhere the ability to set an environment (like "Cave") from within TGB, so as far as I know, if any functionality here is broken by my upgrade steps, it was never exposed to script anyway.
1) Get the OpenAL 1.1 distrubition:
http://connect.creativelabs.com/openal/Downloads/OpenAL11CoreSDK.zip
Or use the link below to get a list of all downloads if the above link doesn't work, and click on "OpenALCore11SDK"
http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx
2 Open up your TGB folder, and open engine/lib/openal/win32/al
Drag all the files inside this folder to some folder on your desktop. (Just in case anything goes wrong, you can restore them instead of deleting them.)
Now drag all the files inside the "include" folder into the Torque engine's "al" folder. Also drag open up "libs/Win32" in the OpenAL distrubion, and drag the stuff here into the Torque "al" folder as well.
3) Open up the Torque Visual C++ project and drag the OpenAL32.lib file into the project.
4) Open audio/AudioDatablock.cc and add this near the top of the file:
5) In audio/audio.cc, modify OpenALInit()'s alcOpenDevice() call to use ALChar* instead of ALByte * as seen below:
6) In platform/platformAL.h, change the beginning if block to this:
7) Remove "OpenAL32.dll" from your game's distribution. Do this by opening up tgb/gameData/T2DProject and moving the file somewhere else, like to your desktop.
But I have a question. The final step in upgrading was to remove "OpenAL32.dll" from the game's folder, since OpenAL 1.1 has it as a library built-in to the project. But: I also discovered that by removing this file from *previous* versions of my game (i.e. ones with the older version of OpenAL), this ALSO fixes the sound problems I was having... *without* upgrading to OpenAL. So my questions are: 1) What does a ".dll" file do? (I'm a Mac guy, illiterate to Windows.) 2) By removing this file, was the game simply defaulting to some installed copy of OpenAL on Windows (that I'm assuming was newer). 3) How can I know if I "upgraded" OpenAL in TGB correctly, since removing this the .dll seems to be what "fixed" it, versus the changes I made to the engine?
In any case, here are the steps I followed to upgrade to OpenAL. After doing these steps, if you do not remove OpenAL32.dll from the game's folder before running, the game will crash, which leads me to believe I probably did the upgrade correctly. (i.e. the OpenAL library is now compiled into the TGB app instead of shipping separately as a .dll file.) But this is an assumption, since I don't really know what a .dll file is.
-------------
UPGRADING TGB to OpenAL 1.1
DISCLAIMER: Step 4 gets the engine to compile, but might eliminate some audio functionality used for different "environments." However, I've never seen documented anywhere the ability to set an environment (like "Cave") from within TGB, so as far as I know, if any functionality here is broken by my upgrade steps, it was never exposed to script anyway.
1) Get the OpenAL 1.1 distrubition:
http://connect.creativelabs.com/openal/Downloads/OpenAL11CoreSDK.zip
Or use the link below to get a list of all downloads if the above link doesn't work, and click on "OpenALCore11SDK"
http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx
2 Open up your TGB folder, and open engine/lib/openal/win32/al
Drag all the files inside this folder to some folder on your desktop. (Just in case anything goes wrong, you can restore them instead of deleting them.)
Now drag all the files inside the "include" folder into the Torque engine's "al" folder. Also drag open up "libs/Win32" in the OpenAL distrubion, and drag the stuff here into the Torque "al" folder as well.
3) Open up the Torque Visual C++ project and drag the OpenAL32.lib file into the project.
4) Open audio/AudioDatablock.cc and add this near the top of the file:
// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT
enum
{
EAX_ENVIRONMENT_GENERIC,
EAX_ENVIRONMENT_PADDEDCELL,
EAX_ENVIRONMENT_ROOM,
EAX_ENVIRONMENT_BATHROOM,
EAX_ENVIRONMENT_LIVINGROOM,
EAX_ENVIRONMENT_STONEROOM,
EAX_ENVIRONMENT_AUDITORIUM,
EAX_ENVIRONMENT_CONCERTHALL,
EAX_ENVIRONMENT_CAVE,
EAX_ENVIRONMENT_ARENA,
EAX_ENVIRONMENT_HANGAR,
EAX_ENVIRONMENT_CARPETEDHALLWAY,
EAX_ENVIRONMENT_HALLWAY,
EAX_ENVIRONMENT_STONECORRIDOR,
EAX_ENVIRONMENT_ALLEY,
EAX_ENVIRONMENT_FOREST,
EAX_ENVIRONMENT_CITY,
EAX_ENVIRONMENT_MOUNTAINS,
EAX_ENVIRONMENT_QUARRY,
EAX_ENVIRONMENT_PLAIN,
EAX_ENVIRONMENT_PARKINGLOT,
EAX_ENVIRONMENT_SEWERPIPE,
EAX_ENVIRONMENT_UNDERWATER,
EAX_ENVIRONMENT_DRUGGED,
EAX_ENVIRONMENT_DIZZY,
EAX_ENVIRONMENT_PSYCHOTIC,
EAX_ENVIRONMENT_UNDEFINED,
EAX_ENVIRONMENT_COUNT
}; 5) In audio/audio.cc, modify OpenALInit()'s alcOpenDevice() call to use ALChar* instead of ALByte * as seen below:
bool OpenALInit()
{
OpenALShutdown();
if(!OpenALDLLInit())
return false;
// Open a device
#ifdef TORQUE_OS_LINUX
const char* deviceSpecifier =
Con::getVariable("Pref::Unix::OpenALSpecifier");
if (dStrlen(deviceSpecifier) == 0)
// use SDL for audio output by default
deviceSpecifier = "'((devices '(sdl)))";
mDevice = (ALCdevice *)alcOpenDevice((ALubyte*)deviceSpecifier);
#else
mDevice = (ALCdevice *)alcOpenDevice((ALCchar*)NULL); // MODIFIED TO WORK WITH OPENAL 1.1
// mDevice = (ALCdevice *)alcOpenDevice((ALubyte*)NULL);
#endif 6) In platform/platformAL.h, change the beginning if block to this:
#if defined(TORQUE_OS_MAC) #include <OpenAL/al.h> #include <OpenAL/alc.h> #include <OpenAL/altypes.h> #include <OpenAL/alctypes.h> #include <OpenAL/eaxtypes.h> #else // declare externs of the AL fns here. #include "al/al.h" #include "al/alc.h" #include "al/efx.h" #include "al/EFX-Util.h" //#include "al/altypes.h" //#include "al/alctypes.h" //#include "al/eaxtypes.h" #define AL_FUNCTION(fn_return,fn_name,fn_args, fn_value) extern fn_return (FN_CDECL *fn_name)fn_args; //#include "al/al_func.h" //#include "al/alc_func.h" //#include "al/eax_func.h" #undef AL_FUNCTION #endif
7) Remove "OpenAL32.dll" from your game's distribution. Do this by opening up tgb/gameData/T2DProject and moving the file somewhere else, like to your desktop.
#2
If you could find out what version of OpenAL is on your system, I'd be interested to know.
06/17/2009 (7:32 pm)
If OpenAL32.dll is missing from the game directory it defaults to the systems version. (system32 folder in windows)If you could find out what version of OpenAL is on your system, I'd be interested to know.
#3
6.14.357.24
I have no idea what that means. The other odd thing is it says the file was created yesterday, the same time I recompiled OpenAL 1.1 into my version of the engine and removed the .dll from the TGB folder. It is possible OpenAL32.dll in the *System* didn't exist prior, and was added by my game at run-time? OR was overwritten by my game to a new version?
Should I ship my game with this .dll in the game's folder???
BTW, I am running Windows on an Intel Mac, using Boot Camp. I don't know if that has anything to do with anything. (Boot Camp does install some custom drivers for various things after you install Windows.)
06/18/2009 (9:39 am)
Okay, will doing Properties->File Version gives me this:6.14.357.24
I have no idea what that means. The other odd thing is it says the file was created yesterday, the same time I recompiled OpenAL 1.1 into my version of the engine and removed the .dll from the TGB folder. It is possible OpenAL32.dll in the *System* didn't exist prior, and was added by my game at run-time? OR was overwritten by my game to a new version?
Should I ship my game with this .dll in the game's folder???
BTW, I am running Windows on an Intel Mac, using Boot Camp. I don't know if that has anything to do with anything. (Boot Camp does install some custom drivers for various things after you install Windows.)
#4
Can anyone verify that I've done the upgrade procedure correctly? That if I ship this game with this newer .dll file included, it won't crash or behave badly on systems different from my own?
06/18/2009 (9:47 am)
Just tested putting the OpenAL32.dll file from the Windows system into my TGB folder, and running both old versions of the game (compiled with stock TGB that use OpenAL 1.0) as well as the new version using the changes outlined above, and both seem to run perfectly.Can anyone verify that I've done the upgrade procedure correctly? That if I ship this game with this newer .dll file included, it won't crash or behave badly on systems different from my own?
#5
The only thing that should overwrite the system OpenAL32.dll is the OpenAL installer, yourself manually, or Windows Update... (Not totally sure about Windows Update, but I believe Microsoft included an updated version at one point in a service pack.)
06/18/2009 (7:21 pm)
If using OpenAL You should have the version of OpenAL32.dll that you compiled with in your root game folder.The only thing that should overwrite the system OpenAL32.dll is the OpenAL installer, yourself manually, or Windows Update... (Not totally sure about Windows Update, but I believe Microsoft included an updated version at one point in a service pack.)
#6
No, if you did not compile the new version of OpenAL into TGB, just including the .dll in the stock 1.7.4 version of TGB will not help. In fact testing across three separate Windows machines shows that doing so will cause sound to not function at all. None of said machines had OpenAL installed to begin with. When the stock version of TGB was used sound worked. Looks like for now, recompiling TGB is the only good fix available. (though I have yet to test this on other machines, once I follow the steps above, if it turns out that does not work as well, I will reply again to this post)
My guess is that if the engine does not recognize the .dll given, then it tries to default to the one in the windows/system32 folder.
08/08/2009 (10:44 pm)
Sorry for the thread necromancy, but I feel this issue is annoying enough to comment on.Quote:Just tested putting the OpenAL32.dll file from the Windows system into my TGB folder, and running both old versions of the game (compiled with stock TGB that use OpenAL 1.0) as well as the new version using the changes outlined above, and both seem to run perfectly.
Can anyone verify that I've done the upgrade procedure correctly? That if I ship this game with this newer .dll file included, it won't crash or behave badly on systems different from my own?
No, if you did not compile the new version of OpenAL into TGB, just including the .dll in the stock 1.7.4 version of TGB will not help. In fact testing across three separate Windows machines shows that doing so will cause sound to not function at all. None of said machines had OpenAL installed to begin with. When the stock version of TGB was used sound worked. Looks like for now, recompiling TGB is the only good fix available. (though I have yet to test this on other machines, once I follow the steps above, if it turns out that does not work as well, I will reply again to this post)
My guess is that if the engine does not recognize the .dll given, then it tries to default to the one in the windows/system32 folder.
#7
I had attempted to recompile TGB with updated OpenAL source files. Did I do something wrong? How do I know if I recompiled it correctly?
08/08/2009 (11:39 pm)
Okay, so if I have not upgraded it correctly, how do I do it right?I had attempted to recompile TGB with updated OpenAL source files. Did I do something wrong? How do I know if I recompiled it correctly?
#8
If you want to find out yourself though, all you need is a windows box that hasn't installed OpenAL.
08/09/2009 (3:24 am)
I haven't tested the compiled version yet, that's next on my to do list. I'm just saying if you stick in the .dll without recompiling TGB, that will not work.If you want to find out yourself though, all you need is a windows box that hasn't installed OpenAL.
#9
the dll that comes with Torque does one thing only and thats preventing you from start any product at all on Win64
08/09/2009 (5:34 am)
the most important thing if you don't want to piss Win64 users is: remove the openal dll and point users to the open al download page.the dll that comes with Torque does one thing only and thats preventing you from start any product at all on Win64
#10
Also need to be commented out. Also, getting these error messages when compiling:
4>..\..\source\platformWin32\winOpenAL.cc(49) : error C2065: 'ALubyte' : undeclared identifier
4>..\..\source\platformWin32\winOpenAL.cc(49) : error C2059: syntax error : ')'
4>..\..\source\platformWin32\winOpenAL.cc(49) : error C3861: 'alGetProcAddress': identifier not found
Only errors I can't resolve. Line reads
Did you run across this?
08/10/2009 (9:23 am)
Tried out the steps above, no dice as for as getting the engine to compile. I think you forgot to mention that all of the lines in TorqueGameBuilder-1.7.4\engine\source\platformWin32\winOpenAL.cc that go:#include "al/al_func.h" #include "al/alc_func.h"
Also need to be commented out. Also, getting these error messages when compiling:
4>..\..\source\platformWin32\winOpenAL.cc(49) : error C2065: 'ALubyte' : undeclared identifier
4>..\..\source\platformWin32\winOpenAL.cc(49) : error C2059: syntax error : ')'
4>..\..\source\platformWin32\winOpenAL.cc(49) : error C3861: 'alGetProcAddress': identifier not found
Only errors I can't resolve. Line reads
fnAddress = alGetProcAddress( (ALubyte*)name );
Did you run across this?
#11
fnAddress = alGetProcAddress( (ALubyte*)name );
For some reason mine compiles just fine. :-/
As for commenting out al/al_func.h, nope, those aren't commented out in my copy. Maybe that's why you're getting problems on line 49 and I'm not. Why did you think you had to comment these lines out?
I've just started beta testing though, and at least 3 testers aren't getting any audio at all. Despite the fact that the engine did recompile with OpenAL 1.1 sources, and I'm shipping the game with the new .dll file that came with OpenAL 1.1. I'm not sure what to do now. :-(
08/24/2009 (2:58 pm)
Those errors are all on line 49. Mine reads the same as yours:fnAddress = alGetProcAddress( (ALubyte*)name );
For some reason mine compiles just fine. :-/
As for commenting out al/al_func.h, nope, those aren't commented out in my copy. Maybe that's why you're getting problems on line 49 and I'm not. Why did you think you had to comment these lines out?
I've just started beta testing though, and at least 3 testers aren't getting any audio at all. Despite the fact that the engine did recompile with OpenAL 1.1 sources, and I'm shipping the game with the new .dll file that came with OpenAL 1.1. I'm not sure what to do now. :-(
#12
08/24/2009 (3:01 pm)
Hmm, looking over my original post, I see that in a different file I did replace ALubyte* with ALCchar*. You might try that in the line that's giving you trouble. Maybe *I* did something wrong, given that my al/al_func.h and so on header includes aren't commented out, but my project still compiles?
#13
I also don't think your solution of commenting out certain headers in this file will work properly, either. There are quite a number of times when it has things like this:
I do not completely understand what is going on here, but it looks like these blocks of code do some magic, using the macro and included headers to define certain functions. If you don't include the proper header files here, it won't define what it should be defining. I'm going to compare old OpenAL headers against the new ones, and see what I think should be included here, and will post my findings soon.
08/24/2009 (3:25 pm)
Okay, in looking at this more, I'm reasonably sure I never got my copy of winOpenAL.cc to recompile. This is probably part of my problem.I also don't think your solution of commenting out certain headers in this file will work properly, either. There are quite a number of times when it has things like this:
// Define the OpenAL and Extension Stub functions
#define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_return stub_##fn_name fn_args{ fn_value }
#include "al/al_func.h"
#include "al/alc_func.h"
#include "al/eax_func.h"
#undef AL_FUNCTIONI do not completely understand what is going on here, but it looks like these blocks of code do some magic, using the macro and included headers to define certain functions. If you don't include the proper header files here, it won't define what it should be defining. I'm going to compare old OpenAL headers against the new ones, and see what I think should be included here, and will post my findings soon.
#14
1)Any files like "altypes.h" are deprecated. Comment them out when you see them, for example:
2) I am now thinking that the various "func" files were *not* part of OpenAL 1.0, but are rather Torque-specific files! As such they can not be removed, but should be copied from your old "al" folder into your new one. Hopefully leaving them as-is still works, I'm not sure. :-/
And so when you see stuff like this, you do NOT want to comment it out!
I will be trying this and posting results soon.
I'm not sure what a "Stub function" is but I"m guessing Torque uses these files to export to TorqueScript the names and parameters of the available OpenAL functions you can use from script. Can anyone confirm/deny this?
08/24/2009 (3:52 pm)
Okay, here are my *revised* thoughts about what headers to use and not use.1)Any files like "altypes.h" are deprecated. Comment them out when you see them, for example:
//#include "al/altypes.h" // OLD Header. Now part of al.h //#include "al/alctypes.h" // OLD header. Now part of alc.h #define INITGUID //#include "al/eaxtypes.h" // OLD header. Now part of efx.h???
2) I am now thinking that the various "func" files were *not* part of OpenAL 1.0, but are rather Torque-specific files! As such they can not be removed, but should be copied from your old "al" folder into your new one. Hopefully leaving them as-is still works, I'm not sure. :-/
And so when you see stuff like this, you do NOT want to comment it out!
#include "al/al_func.h" #include "al/alc_func.h" #include "al/eax_func.h"
I will be trying this and posting results soon.
I'm not sure what a "Stub function" is but I"m guessing Torque uses these files to export to TorqueScript the names and parameters of the available OpenAL functions you can use from script. Can anyone confirm/deny this?
#15
I copied over altypes.h and alctypes.h from the original TGB 1.7.4 distribution (in the "al" folder"), but modified them, cutting code from "al.h" and "alc.h" and pasting it into altypes.h and alctypes.h respectively. This is because the OpenAL 1.1 distribution combines what was previously altypes.h and al.h into one file. Same for alctypes.h and alc.h. So we have to split them back into how they were before. I also had to add this to alctypes.h after the ALCvoid definition:
typedef ALCvoid ALCdevice;
typedef ALCvoid ALCcontext;
I also got rid of EAX. I simply commented out all "al/eax_func.h" includes, and modified bindEAXFunction() in winOpenAL.cc to do nothing:
The top of my PlatformAL.h file now reads:
and the top of my winOpenAL.cc file now reads:
Also, the line:
fnAddress = alGetProcAddress( (ALubyte*)name );
no longer needs changing. Same for the line in Audio.cc in my original instructions. It should remain ALubyte.
That got it to compile for me. I have yet to test it with my beta testers to see if it actually *works* now though. (It has always worked on my own setup, even when I did it wrong, so I can't reliably test on my own system.)
08/24/2009 (5:33 pm)
OKay, so here's what I did:I copied over altypes.h and alctypes.h from the original TGB 1.7.4 distribution (in the "al" folder"), but modified them, cutting code from "al.h" and "alc.h" and pasting it into altypes.h and alctypes.h respectively. This is because the OpenAL 1.1 distribution combines what was previously altypes.h and al.h into one file. Same for alctypes.h and alc.h. So we have to split them back into how they were before. I also had to add this to alctypes.h after the ALCvoid definition:
typedef ALCvoid ALCdevice;
typedef ALCvoid ALCcontext;
I also got rid of EAX. I simply commented out all "al/eax_func.h" includes, and modified bindEAXFunction() in winOpenAL.cc to do nothing:
static bool bindEAXFunctions()
{
bool result = true;
// #define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) result &= bindExtensionFunction( *(void**)&fn_name, #fn_name);
// #include "al/eax_func.h"
// #undef AL_FUNCTION
return result;
}The top of my PlatformAL.h file now reads:
#ifndef _PLATFORMAL_H_ #define _PLATFORMAL_H_ #ifndef _PLATFORM_H_ #include "platform/platform.h" #endif #if defined(TORQUE_OS_MAC) #include <OpenAL/al.h> #include <OpenAL/alc.h> #include <OpenAL/altypes.h> #include <OpenAL/alctypes.h> #include <OpenAL/eaxtypes.h> #else // declare externs of the AL fns here. #include "al/altypes.h" #include "al/alctypes.h" ///#include "al/eaxtypes.h" #define AL_FUNCTION(fn_return,fn_name,fn_args, fn_value) extern fn_return (FN_CDECL *fn_name)fn_args; #include "al/al_func.h" #include "al/alc_func.h" //#include "al/eax_func.h" #undef AL_FUNCTION #endif
and the top of my winOpenAL.cc file now reads:
#include "platformWin32/platformWin32.h"
#include "console/console.h"
#include "al/altypes.h"
#include "al/alctypes.h"
#define INITGUID
//#include "al/eaxtypes.h"
// Define the OpenAL and Extension Stub functions
#define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_return stub_##fn_name fn_args{ fn_value }
#include "al/al_func.h"
#include "al/alc_func.h"
//#include "al/eax_func.h"
#undef AL_FUNCTION
// Declare the OpenAL and Extension Function pointers
// And initialize them to the stub functions
#define AL_FUNCTION(fn_return,fn_name,fn_args, fn_value) fn_return (*fn_name)fn_args = stub_##fn_name;
#include "al/al_func.h"
#include "al/alc_func.h"
//#include "al/eax_func.h"
#undef AL_FUNCTIONAlso, the line:
fnAddress = alGetProcAddress( (ALubyte*)name );
no longer needs changing. Same for the line in Audio.cc in my original instructions. It should remain ALubyte.
That got it to compile for me. I have yet to test it with my beta testers to see if it actually *works* now though. (It has always worked on my own setup, even when I did it wrong, so I can't reliably test on my own system.)
#16
08/24/2009 (5:34 pm)
Oh, and also yes, what I wrote in my previous post is required too -- you need to keep the al_func.h and alc_func.h files from TGB 1.7.4 in the project, and not comment out the lines that #include them.
#17
I had my beta testers test the game after recompiling as described above, and it still didn't work. Fortunately, one of my testers figured out what the problem was. I needed to copy this file:
wrap_oal.dll
from Window's system32 folder into my game's folder, so it's there right along with OpenAL32.dll. This is apparently a software solution that kicks in if the computer's hardware sound card can't be used by OpenAL for whatever reason.
Now everything works great!
08/25/2009 (5:11 pm)
**Update**I had my beta testers test the game after recompiling as described above, and it still didn't work. Fortunately, one of my testers figured out what the problem was. I needed to copy this file:
wrap_oal.dll
from Window's system32 folder into my game's folder, so it's there right along with OpenAL32.dll. This is apparently a software solution that kicks in if the computer's hardware sound card can't be used by OpenAL for whatever reason.
Now everything works great!
Torque Owner Vern Jensen
http://vlaurie.com/computers2/Articles/dll.htm
But I'm still wondering: 1) Why did my audio problems go away by deleting OpenAL32.dll from the TGB folder *without* upgrading OpenAL? Shouldn't sound have just stopped working instead (or worse, crashed), since presumably the code to handle sounds would've been gone?
Or does Torque default to some non-OpenAL system for handling sounds when OpenAL32.dll is missing from the directory? If this is true, perhaps my attempt to "upgrade" OpenAL have actually resulted in TGB using some other method of playing audio instead.