singe/sound/sound.cpp
2019-11-11 14:53:02 -06:00

882 lines
23 KiB
C++

/*
* sound.cpp
*
* Copyright (C) 2001 Matt Ownby
*
* This file is part of DAPHNE, a laserdisc arcade game emulator
*
* DAPHNE is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* DAPHNE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// DAPHNE
// The sound code in this file uses SDL
#ifdef DEBUG
#include <assert.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include "SDL.h"
#include "SDL_audio.h"
#include "sound.h"
//#include "sn_intf.h"
//#include "pc_beeper.h"
//#include "gisound.h"
//#include "dac.h"
//#include "tonegen.h"
#include "samples.h"
#include "mix.h"
#include "../io/conout.h"
#include "../io/mpo_mem.h"
#include "../io/numstr.h"
#include "../game/game.h"
#include "../daphne.h"
#include "../ldp-out/ldp-vldp.h" // added by JFA for -startsilent
sample_s g_samples[MAX_NUM_SOUNDS] = { { 0 } };
sample_s g_sample_saveme; // the special saveme wav which is loaded independently of any game
bool g_sound_enabled = true; // whether sound is enabled
bool g_bSoundMuted = false; // whether sound is muted
struct sounddef *g_soundchip_head = NULL; // pointer to the first sound chip in our linked list of chips's
unsigned int g_uSoundChipNextID = 0; // the idea that the next soundchip to get added will get (also usually indicates how many sound chips have been added, but not if a soundchip gets deleted)
// callback to actually do the mixing
void (*g_soundmix_callback)(Uint8* stream, int length) = mixNone;
// The # of samples in the sound buffer
// Matt prefers 1024 but some people (cough Warren) can't handle it haha
// but now it can be changed from the command line
Uint16 g_u16SoundBufSamples = 2048;
// # of bytes each individual sound chip should be allocated for its buffer
unsigned int g_uSoundChipBufSize = g_u16SoundBufSamples * AUDIO_BYTES_PER_SAMPLE;
// the volume (user adjustable) of the VLDP audio stream
unsigned int g_uVolumeVLDP = AUDIO_MAX_VOLUME;
// the volume (user adjustable) of all over sound streams besides VLDP
unsigned int g_uVolumeNonVLDP = AUDIO_MAX_VOLUME;
int cur_wave = 0; // the current wave being played (0 to NUM_DL_BEEPS-1)
bool g_sound_initialized = false; // whether the sound will work if we try to play it
// Macros to help automatically verify that our locks and unlocks are correct
#ifdef DEBUG
bool g_bAudioLocked = false;
#define LOCK_AUDIO assert (!g_bAudioLocked); g_bAudioLocked = true; SDL_LockAudio
#define UNLOCK_AUDIO assert (g_bAudioLocked); g_bAudioLocked = false; SDL_UnlockAudio
#else
#define LOCK_AUDIO SDL_LockAudio
#define UNLOCK_AUDIO SDL_UnlockAudio
#endif // lock audio macros
// added by JFA for -startsilent
void set_sound_mute(bool bMuted)
{
g_bSoundMuted = bMuted;
// only proceed if sound has been initialized
if (g_sound_initialized)
{
LOCK_AUDIO();
// this should set the mixing callback back to something that isn't muted
update_soundchip_volumes();
UNLOCK_AUDIO();
}
}
// end edit
void set_soundbuf_size(Uint16 newbufsize)
{
g_u16SoundBufSamples = newbufsize;
g_uSoundChipBufSize = newbufsize * AUDIO_BYTES_PER_SAMPLE;
// re-allocate all sound buffers since the size has changed
struct sounddef *cur = g_soundchip_head;
while (cur)
{
delete cur->buffer;
cur->buffer = new Uint8 [g_uSoundChipBufSize];
memset(cur->buffer, 0, g_uSoundChipBufSize);
cur->buffer_pointer = cur->buffer;
cur->bytes_left = g_uSoundChipBufSize;
cur = cur->next_soundchip;
}
}
static SDL_AudioSpec specDesired, specObtained;
bool sound_init()
// returns a true on success, false on failure
{
bool result = false;
int audio_rate = AUDIO_FREQ; // rate to mix audio at. This cannot be changed without resampling all .wav's and all .ogg's
Uint16 audio_format = AUDIO_FORMAT;
int audio_channels = AUDIO_CHANNELS;
printline("Initializing sound system ... ");
// if the user has not disabled sound from the command line
if (is_sound_enabled())
{
// if SDL audio initialization was successful
if (SDL_InitSubSystem(SDL_INIT_AUDIO) >= 0)
{
specDesired.callback = audio_callback;
specDesired.channels = audio_channels;
specDesired.format = audio_format;
specDesired.freq = audio_rate;
specDesired.samples = g_u16SoundBufSamples;
specDesired.userdata = NULL;
// this stuff doesn't need to be filled in supposedly ...
specDesired.padding = 0;
specDesired.size = 0;
// if we can open the audio device
if (SDL_OpenAudio(&specDesired, &specObtained) >= 0)
{
// make sure we got what we asked for
if ((specObtained.channels == audio_channels) &&
(specObtained.format == audio_format) &&
(specObtained.freq == audio_rate) &&
(specObtained.callback == audio_callback))
{
// if we can load all our waves, we're set
// If we are supposed to start without playing any sound, then set muted bool here.
// It must come here because add_soundchip (which comes right afterwards) will set the sound mixing callback.
if (get_startsilent())
{
g_bSoundMuted = true;
}
// right before initialization, add the samples 'sound chip', which can (and should be)
// only added once, so we need not track its ID (we call its functions directly)
struct sounddef soundchip;
soundchip.type = SOUNDCHIP_SAMPLES;
add_soundchip(&soundchip);
// initialize sound chips
init_soundchip();
if (specObtained.samples != g_u16SoundBufSamples)
{
string strWarning = "WARNING : requested " + numstr::ToStr(g_u16SoundBufSamples) +
" samples for sound buffer, but got " + numstr::ToStr(specObtained.samples) + " samples";
printline(strWarning.c_str());
// reset memory allocations
set_soundbuf_size(specObtained.samples);
}
result = true;
g_sound_initialized = true;
// enable the audio callback (this should come last to be safe)
SDL_PauseAudio(0); // start mixing! :)
} // end if audio specs are correct
else
{
printline("ERROR: unable to obtain desired audio configuration");
}
} // end if audio device could be opened ...
// if audio device could not be opened (ie no sound card)
else
{
outstr("WARNING: Audio device could not be opened: ");
printline(SDL_GetError());
g_sound_enabled = false;
}
} // end if sound initializtion worked
} // end if sound is enabled
// if sound isn't enabled, then we act is if sound initialization worked so daphne doesn't quit
if (!is_sound_enabled())
{
result = true;
}
return(result);
}
// shuts down the sound subsystem
void sound_shutdown()
{
// shutdown sound only if we previously initialized it
if (g_sound_initialized)
{
printline("Shutting down sound system...");
SDL_PauseAudio(1);
SDL_CloseAudio();
free_waves();
shutdown_soundchip();
g_sound_initialized = 0;
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
}
// plays a sample, returns true on success
bool sound_play(Uint32 whichone)
{
bool result = false;
// only play a sound if sound has been initialized (and if whichone points to a valid wav)
if (is_sound_enabled() && (whichone < MAX_NUM_SOUNDS))
{
samples_play_sample(g_samples[whichone].pu8Buf, g_samples[whichone].uLength, g_samples[whichone].uChannels);
result = true;
}
return(result);
}
// plays the 'saveme' sound
bool sound_play_saveme()
{
bool result = false;
if (is_sound_enabled())
{
samples_play_sample(g_sample_saveme.pu8Buf, g_sample_saveme.uLength);
result = true;
}
return result;
}
// loads the wave files into the wave structure
// returns 0 if failure, or non-zero if success
int load_waves()
{
Uint32 i = 0;
int result = 1;
string filename = "";
SDL_AudioSpec spec;
for (; (i < g_game->get_num_sounds()) && result; i++)
{
filename = "sound/";
filename += g_game->get_sound_name(i);
// initialize so that shutting down succeeds
g_samples[i].pu8Buf = NULL;
g_samples[i].uLength = 0;
// if loading the .wav file succeeds
if (SDL_LoadWAV(filename.c_str(), &spec, &g_samples[i].pu8Buf, &g_samples[i].uLength))
{
// make sure audio specs are correct
if (((spec.channels == AUDIO_CHANNELS) || (spec.channels == 1)) &&
(spec.freq == AUDIO_FREQ) &&
(spec.format == AUDIO_S16))
{
g_samples[i].uChannels = spec.channels;
}
// else specs are not correct
else
{
outstr("ERROR: Audio specs are not correct for ");
printline(filename.c_str());
result = 0;
}
} // end if loading worked ...
// TODO : add .OGG loading support in here
else
{
outstr("ERROR: Could not open sample file ");
printline(filename.c_str());
result = 0;
}
}
// load "saveme" sound in
if (!SDL_LoadWAV("sound/saveme.wav", &spec, &g_sample_saveme.pu8Buf, &g_sample_saveme.uLength))
{
printline("Loading 'saveme.wav' failed...");
result = 0;
}
// if something went wrong, eject ...
if (!result)
{
free_waves();
}
return(result);
}
// free all allocated waves
void free_waves()
{
unsigned int i = 0;
for (; i < g_game->get_num_sounds(); i++)
{
// don't de-allocate a sound if it hasn't been allocated
if (g_samples[i].pu8Buf)
{
SDL_FreeWAV(g_samples[i].pu8Buf);
g_samples[i].pu8Buf = NULL;
}
}
if (g_sample_saveme.pu8Buf)
{
SDL_FreeWAV(g_sample_saveme.pu8Buf);
g_sample_saveme.pu8Buf = NULL;
}
}
int get_sound_initialized()
{
return(g_sound_initialized);
}
void set_sound_enabled_status (bool value)
{
g_sound_enabled = value;
}
bool is_sound_enabled()
{
return g_sound_enabled;
}
// NOTE : this is called by the game driver, so it can be called even if sound is disabled
unsigned int add_soundchip(struct sounddef *candidate)
{
LOCK_AUDIO(); // safety precaution, we don't want callback running during this function
struct sounddef *cur = NULL;
// if this is the first sound chip to be added to the list
if (!g_soundchip_head)
{
g_soundchip_head = new struct sounddef; // allocate a new sound chip, assume allocation is successful
cur = g_soundchip_head; // point to the new sound chip so we can populate it with info
}
// else we have to move to the end of the list
else
{
cur = g_soundchip_head;
// go to the last sound chip in the list
while (cur->next_soundchip)
{
cur = cur->next_soundchip;
}
cur->next_soundchip = new struct sounddef; // allocate a new sound chip at the end of our list
cur = cur->next_soundchip; // point to the new sound chip so we can populate it with info
}
// now we must copy over the relevant info
memcpy(cur, candidate, sizeof(struct sounddef)); // copy entire thing over
cur->id = g_uSoundChipNextID;
cur->internal_id = 0; // sensible initial value
// This function will be called before the user has a chance to modify the volumes,
// so we will initialize them all to sensible defaults.
cur->uDriverVolume[0] = cur->uDriverVolume[1] =
cur->uBaseVolume[0] = cur->uBaseVolume[1] =
cur->uVolume[0] = cur->uVolume[1] = AUDIO_MAX_VOLUME;
++g_uSoundChipNextID;
cur->next_soundchip = NULL;
cur->bNeedsConstantUpdates = false; // sensible default
// create a buffer for each chip
cur->buffer = new Uint8 [g_uSoundChipBufSize];
cur->buffer_pointer = cur->buffer;
cur->bytes_left = g_uSoundChipBufSize;
cur->init_callback = NULL;
cur->shutdown_callback = NULL;
cur->stream_callback = NULL;
cur->writedata_callback = NULL;
cur->write_ctrl_data_callback = NULL;
memset(cur->buffer, 0, g_uSoundChipBufSize);
// now we must assign the appropriate callbacks
switch (cur->type)
{
case SOUNDCHIP_SAMPLES:
cur->init_callback = samples_init;
cur->shutdown_callback = samples_shutdown;
cur->stream_callback = samples_get_stream;
break;
case SOUNDCHIP_VLDP:
// we only need to define stream_callback, everything else is handled by ldp-vldp
cur->stream_callback = ldp_vldp_audio_callback;
break;
default:
printline("FATAL ERROR : unknown sound chip added");
set_quitflag(); // force user to deal with this problem
break;
}
// calculate mixing callback, adjust volume, recalculate rshift
// NOTE : this should come last in this function
update_soundchip_volumes();
UNLOCK_AUDIO();
return cur->id;
}
bool delete_soundchip(unsigned int id)
{
bool bSuccess = false;
struct sounddef *cur = g_soundchip_head;
struct sounddef *prev = NULL;
LOCK_AUDIO();
// if 1 or more sound chips exists ...
while (cur)
{
struct sounddef *pNext = cur->next_soundchip;
// if we found a match, then delete it...
if (cur->id == id)
{
// if there is a shutdown callback defined, call it
if (cur->shutdown_callback)
{
cur->shutdown_callback(cur->internal_id);
}
// if cur != g_soundchip_head in other words ...
if (prev != NULL)
{
// restore the chain that we're about to break
prev->next_soundchip = cur->next_soundchip;
}
delete [] cur->buffer;
delete cur;
// if we just deleted the head, then make the next soundchip be the head
if (cur == g_soundchip_head)
{
g_soundchip_head = pNext;
}
bSuccess = true;
break;
}
prev = cur;
cur = cur->next_soundchip;
}
UNLOCK_AUDIO();
return bSuccess;
}
void init_soundchip()
{
#ifdef DEBUG
assert(is_sound_enabled());
#endif
LOCK_AUDIO(); // safety precaution, we don't want callback running during this function
if (g_soundchip_head)
{
struct sounddef *cur = g_soundchip_head;
while (cur)
{
// only initialize if the callback exists
if (cur->init_callback)
{
cur->internal_id = cur->init_callback(cur->hz);
if (cur->internal_id == -1)
{
printline("sound.cpp Error : sound chip failed to initialize");
set_quitflag(); // force dev to deal with this problem
}
// else everything initialized correctly
}
cur = cur->next_soundchip;
}
}
UNLOCK_AUDIO();
}
// Mixing callback
// USED WHEN : all audio is muted
void mixMute(Uint8 *stream, int length)
{
memset(stream, 0, length);
}
// Mixing callback
// USED WHEN: there is only 1 sound chip, then there is no need to do any mixing
void mixNone(Uint8 *stream, int length)
{
if (g_soundchip_head != NULL)
{
memcpy(stream, g_soundchip_head->buffer, length);
}
}
// Mixing callback
// USED WHEN: there are more than 1 sound chip, but all volumes are maximum
void mixWithMaxVolume(Uint8 *stream, int length)
{
#ifdef DEBUG
assert(g_soundchip_head);
#endif // DEBUG
// this is a dangerous trick (casting one struct to another) in order to get us extra speed
g_pMixBufs = (struct mix_s *) g_soundchip_head;
g_pSampleDst = stream;
g_uBytesToMix = length;
g_mix_func();
/*
struct sounddef *cur;
// mix all sound chip buffers together
// NOTE : this algorithm is accurate but NOT OPTIMIZED and probably should be optimized,
// because it is called constantly.
for (int sample = 0; sample < (length >> 1); sample += 2)
{
Sint32 mixed_sample_1 = 0, mixed_sample_2 = 0; // left/right channels
cur = g_soundchip_head;
while (cur)
{
mixed_sample_1 += LOAD_LIL_SINT16(((Sint16 *) cur->buffer) + sample);
mixed_sample_2 += LOAD_LIL_SINT16(((Sint16 *) cur->buffer) + sample + 1);
cur = cur->next_soundchip;
}
DO_CLIP(mixed_sample_1);
DO_CLIP(mixed_sample_2);
// note: sample2 needs to be on top because this is little endian, hence LSB
Uint32 val_to_store = (((Uint16) mixed_sample_2) << 16) | (Uint16) mixed_sample_1;
STORE_LIL_UINT32(stream, val_to_store);
stream += 4;
}
*/
}
// Mixing callback
// USED WHEN: there are more than 1 sound chip, and volumes are variable
// (this is the slowest callback)
void mixWithMults(Uint8 *stream, int length)
{
struct sounddef *cur;
// mix all sound chip buffers together
// NOTE : this algorithm is accurate but NOT OPTIMIZED and probably should be optimized,
// because it is called constantly.
for (int sample = 0; sample < (length >> 1); sample += 2)
{
Sint32 mixed_sample_1 = 0, mixed_sample_2 = 0; // left/right channels, 32-bit to support adding many 16-bit samples
cur = g_soundchip_head;
while (cur)
{
// multiply by the volume and then dividing by the max volume (by shifting right, which is much faster)
mixed_sample_1 += (Sint16) ((LOAD_LIL_SINT16(((Sint16 *) cur->buffer) + sample) * cur->uVolume[0]) >> AUDIO_MAX_VOL_POWER);
mixed_sample_2 += (Sint16) ((LOAD_LIL_SINT16(((Sint16 *) cur->buffer) + sample + 1) * cur->uVolume[1]) >> AUDIO_MAX_VOL_POWER);
cur = cur->next_soundchip;
}
DO_CLIP(mixed_sample_1);
DO_CLIP(mixed_sample_2);
Uint32 val_to_store = (((Uint16) mixed_sample_2) << 16) | (Uint16) mixed_sample_1;
STORE_LIL_UINT32(stream, val_to_store);
stream += 4;
}
}
void audio_callback ( void *data, Uint8 *stream, int length )
{
// now go through the sound chips and mix them in
struct sounddef *cur = g_soundchip_head;
// fill remaining buffer space for each sound chip
while (cur)
{
#ifdef DEBUG
assert(cur->stream_callback != NULL); // every sound chip will have to supply this
#endif
cur->stream_callback(cur->buffer_pointer, cur->bytes_left, cur->internal_id);
cur->buffer_pointer = cur->buffer;
cur->bytes_left = g_uSoundChipBufSize;
cur = cur->next_soundchip;
}
// do the actual mixing now
g_soundmix_callback(stream, length);
}
void audio_writedata(Uint8 id, Uint8 data)
{
// if sound isn't initialized, then the soundchips aren't initialized either
if (g_sound_initialized)
{
LOCK_AUDIO(); // safety precaution, we don't want callback running during this function
struct sounddef *cur = g_soundchip_head;
while (cur)
{
if (cur->id == id)
{
cur->writedata_callback(data, cur->internal_id);
}
cur = cur->next_soundchip;
}
UNLOCK_AUDIO();
}
}
// in case audio_writedata doesn't cut it ...
void audio_write_ctrl_data(unsigned int uCtrl, unsigned int uData, Uint8 id)
{
// if sound isn't initialized, then the soundchips aren't initialized either
if (g_sound_initialized)
{
LOCK_AUDIO();
struct sounddef *cur = g_soundchip_head;
while (cur)
{
if (cur->id == id)
{
cur->write_ctrl_data_callback(uCtrl, uData, cur->internal_id);
}
cur = cur->next_soundchip;
}
UNLOCK_AUDIO();
}
}
void set_soundchip_volume(Uint8 id, unsigned int uChannel, unsigned int uVolume)
{
struct sounddef *cur = g_soundchip_head;
while (cur)
{
if (cur->id == id)
{
set_soundchip_volume(cur, uChannel, uVolume);
break;
}
cur = cur->next_soundchip;
}
}
void set_soundchip_volume(struct sounddef *cur, unsigned int uChannel, unsigned int uVolume)
{
// safety check
if (uChannel < AUDIO_CHANNELS)
{
// safety check
if (uVolume <= AUDIO_MAX_VOLUME)
{
cur->uDriverVolume[uChannel] = uVolume;
LOCK_AUDIO();
update_soundchip_volumes();
UNLOCK_AUDIO();
}
else
{
printline("sound.cpp, set_soundchip_volume() ERROR: volume is out of range, shutting down");
set_quitflag(); // force dev to deal with this :)
}
}
// channel was out of range
else
{
printline("sound.cpp, set_soundchip_volume() ERROR : channel is out of range");
set_quitflag(); // force dev to fix this
}
}
void set_soundchip_vldp_volume(unsigned int uVolume)
{
if (uVolume <= AUDIO_MAX_VOLUME)
{
g_uVolumeVLDP = uVolume;
LOCK_AUDIO();
update_soundchip_volumes();
UNLOCK_AUDIO();
}
else
{
printline("WARNING : request VLDP volume is out of range");
}
}
void set_soundchip_nonvldp_volume(unsigned int uVolume)
{
if (uVolume <= AUDIO_MAX_VOLUME)
{
g_uVolumeNonVLDP = uVolume;
LOCK_AUDIO();
update_soundchip_volumes();
UNLOCK_AUDIO();
}
else
{
printline("WARNING : request non-VLDP volume is out of range");
}
}
unsigned int get_soundchip_nonvldp_volume()
{
return g_uVolumeNonVLDP;
}
// IMPORTANT : assumes LOCK_AUDIO has already been called!!!!
void update_soundchip_volumes()
{
bool bNonMaxVolume = false;
unsigned int uSoundchipCount = 0;
#ifdef DEBUG
assert (g_bAudioLocked == true);
#endif
// If sound is not muted then do some calculations
if (!g_bSoundMuted)
{
struct sounddef *cur = g_soundchip_head;
while (cur)
{
// if this isn't a VLDP chip ...
if (cur->type != SOUNDCHIP_VLDP)
{
cur->uBaseVolume[0] = cur->uBaseVolume[1] = g_uVolumeNonVLDP;
}
// else this is the VLDP chip
else
{
cur->uBaseVolume[0] = cur->uBaseVolume[1] = g_uVolumeVLDP;
}
for (unsigned int uChannel = 0; uChannel < 2; ++uChannel)
{
// The actual volume must take into account the user-defined BaseVolume,
// in case the user requested that the volume be 50% of whatever it would normally be.
// If the base volume is AUDIO_MAX_VOLUME, then the new volume becomes uVolume
cur->uVolume[uChannel] = (cur->uDriverVolume[uChannel] * cur->uBaseVolume[uChannel]) / AUDIO_MAX_VOLUME;
// if we've wound up with a volume that is less than the max
if (cur->uVolume[uChannel] < AUDIO_MAX_VOLUME)
{
bNonMaxVolume = true;
}
}
cur = cur->next_soundchip;
++uSoundchipCount;
}
// if we need to use the most expensive mixing callback...
if (bNonMaxVolume)
{
g_soundmix_callback = mixWithMults;
}
else if (uSoundchipCount > 1)
{
g_soundmix_callback = mixWithMaxVolume;
}
// just 1 soundchip? we can mix super fast in that case
else
{
g_soundmix_callback = mixNone;
}
} // end if sound is not muted
// else sound is muted
else
{
g_soundmix_callback = mixMute;
}
}
void shutdown_soundchip()
{
#ifdef DEBUG
assert(g_sound_initialized);
#endif
LOCK_AUDIO(); // safety precaution, we don't want callback running during this function
struct sounddef *cur = g_soundchip_head;
while (cur)
{
// if there is a shutdown callback defined, call it
if (cur->shutdown_callback)
{
cur->shutdown_callback(cur->internal_id);
}
struct sounddef *temp = cur;
cur = cur->next_soundchip;
delete [] temp->buffer;
delete temp;
}
UNLOCK_AUDIO();
}
void update_soundbuffer()
{
// we don't want to update the sound buffer, if sound isn't initialized
if (g_sound_initialized)
{
// to ensure that the audio callback doesn't get called while we're in this function
LOCK_AUDIO();
struct sounddef *cur = g_soundchip_head;
while (cur)
{
// only update if needed, to save CPU cycles
if (cur->bNeedsConstantUpdates)
{
if (cur->bytes_left >= G_1MS_BUF_SIZE)
{
cur->stream_callback(cur->buffer_pointer, G_1MS_BUF_SIZE, cur->internal_id);
cur->bytes_left -= G_1MS_BUF_SIZE;
cur->buffer_pointer += G_1MS_BUF_SIZE;
}
// else we throw away the new data
// should we handle this some other way?
}
// else doesn't need to be updated so often, so don't do it ...
cur = cur->next_soundchip;
}
UNLOCK_AUDIO();
}
}