1
1
Fork 0
mirror of https://github.com/QB64-Phoenix-Edition/QB64pe.git synced 2024-09-07 15:20:18 +00:00
QB64-PE/internal/c/parts/audio/out/src.c
2022-05-06 13:20:30 -04:00

1891 lines
62 KiB
C

#ifndef DEPENDENCY_AUDIO_OUT
// Stubs:
void snd_mainloop() { return; }
void snd_init() { return; }
void snd_un_init() { return; }
#else
# ifdef QB64_BACKSLASH_FILESYSTEM
# include "AL\\al.h"
# include "AL\\alc.h"
# include <stdio.h>
# include <stdlib.h>
# include <sys\\stat.h>
# include <sys\\types.h>
# include <unistd.h>
# else
# include "AL/al.h"
# include "AL/alc.h"
# include <stdio.h>
# include <stdlib.h>
# include <sys/stat.h>
# include <sys/types.h>
# include <unistd.h>
# endif
// forward refs (with no struct dependencies)
void sndsetup();
void sndclose_now(int32 handle);
void sub__sndstop(int32 handle);
void sub__sndplay(int32);
uint8 *soundwave(double frequency, double length, double volume, double fadein, double fadeout);
void qb64_generatesound(double f, double l, uint8 wait);
void qb64_internal_sndraw(uint8 *data, int32 bytes, int32 block);
double func__sndrawlen(int32 handle, int32 passed);
// global variables
int32 qb64_sndraw_lock = 0;
int32 qb64_internal_sndraw_handle = 0;
int64 qb64_internal_sndraw_lastcall = 0;
int64 qb64_internal_sndraw_prepad = 0;
int64 qb64_internal_sndraw_postpad = 0;
int32 soundwave_bytes = 0;
int32 snd_frequency = 44100;
int32 snd_buffer_size = 16384;
int32 snd_allow_internal = 0; // set this flag before calling snd_... commands with an internal sound
uint8 *soundwave(double frequency, double length, double volume, double fadein, double fadeout) {
// this creates 16bit signed stereo data
sndsetup();
static uint8 *data;
static int32 i;
static int16 x, lastx;
static int16 *sp;
static double samples_per_second;
samples_per_second = snd_frequency;
// calculate total number of samples required
static double samples;
static int32 samplesi;
samples = length * samples_per_second;
samplesi = samples;
if (!samplesi)
samplesi = 1;
soundwave_bytes = samplesi * 4;
data = (uint8 *)malloc(soundwave_bytes);
sp = (int16 *)data;
static int32 direction;
direction = 1;
static double value;
value = 0;
static double volume_multiplier;
volume_multiplier = volume * 32767.0;
static int32 waveend;
waveend = 0;
static double gradient;
// frequency*4.0*length is the total distance value will travel (+1,-2,+1[repeated])
// samples is the number of steps to do this in
if (samples)
gradient = (frequency * 4.0 * length) / samples;
else
gradient = 0; // avoid division by 0
lastx = 1; // set to 1 to avoid passing initial comparison
for (i = 0; i < samplesi; i++) {
x = value * volume_multiplier;
*sp++ = x;
*sp++ = x;
if (x > 0) {
if (lastx <= 0) {
waveend = i;
}
}
lastx = x;
if (direction) {
if ((value += gradient) >= 1.0) {
direction = 0;
value = 2.0 - value;
}
} else {
if ((value -= gradient) <= -1.0) {
direction = 1;
value = -2.0 - value;
}
}
} // i
if (waveend)
soundwave_bytes = waveend * 4;
return (uint8 *)data;
}
int32 wavesize(double length) {
static int32 samples;
samples = length * (double)snd_frequency;
if (samples == 0)
samples = 1;
return samples * 4;
}
void sub_sound(double frequency, double lengthinclockticks) {
sndsetup();
if (new_error)
return;
// note: there are 18.2 clock ticks per second
if ((frequency < 37.0) && (frequency != 0))
goto error;
if (frequency > 32767.0)
goto error;
if (lengthinclockticks < 0.0)
goto error;
if (lengthinclockticks > 65535.0)
goto error;
if (lengthinclockticks == 0.0)
return;
qb64_generatesound(frequency, lengthinclockticks / 18.2, 1);
return;
error:
error(5);
}
struct snd_sequence_struct {
uint16 *data;
int32 data_size;
uint8 channels; // note: more than 2 channels may be supported in the future
uint16 *data_left;
int32 data_left_size;
uint16 *data_right;
int32 data_right_size;
// origins of data (only relevent before src)
uint8 endian; // 0=native, 1=little(Windows, x86), 2=big(Motorola, Xilinx Microblaze, IBM POWER)
uint8 is_unsigned; // 1=unsigned, 0=signed(most common)
int32 sample_rate; // eg. 11025, 22100
int32 bits_per_sample; // eg. 8, 16
int32 references; // number of SND handles dependent on this
};
list *snd_sequences = list_new(sizeof(snd_sequence_struct));
struct snd_struct {
void *lock_offset;
int64 lock_id;
uint8 internal; // 1=internal
uint8 type; // 1=RAW, 2=SEQUENCE
// sequence
snd_sequence_struct *seq;
//----part specific variables----
ALuint al_seq_left_buffer;
ALuint al_seq_right_buffer;
ALenum al_seq_format;
ALsizei al_seq_freq;
ALboolean al_seq_loop;
ALuint al_seq_left_source;
ALuint al_seq_right_source;
//-------------------------------
float volume;
uint8 volume_update;
uint8 close;
uint8 limit_state; // 0=off, 1=awaiting start[duration has been set], 2=waiting for stop point
double limit_duration;
int64 limit_stop_point;
// locks
uint8 setpos_lock_release;
uint8 setpos_update;
float setpos;
float bal_left_x, bal_left_y, bal_left_z;
float bal_right_x, bal_right_y, bal_right_z;
uint8 bal_update;
// usage of buffer depends heavily on type
uint8 *buffer;
int32 buffer_size;
ptrszint *stream_buffer; // pointers to buffers
int32 stream_buffer_last;
int32 stream_buffer_start;
int32 stream_buffer_next;
ALuint al_source;
ALuint *al_buffers; //[4]
uint8 *al_buffer_state; //[4] 0=never used, 1=processing, 2=processed
int32 *al_buffer_index; //[4]
int64 raw_close_time;
// The maximum number of buffers on iOS and on OS X is 1024.
// The maximum number of sources is 32 on iOS and 256 on OS X.
// therefore: inactive sources should be de-initialized & buffers should be 4
uint8 state;
};
# define SND_STATE_STOPPED 0
# define SND_STATE_PLAYING 1
# define SND_STATE_PAUSED 2
list *snd_handles = list_new(sizeof(snd_struct));
void sndsetup() {
static int32 sndsetup_called = 0;
if (!sndsetup_called) {
sndsetup_called = 1;
//...
}
// scan through all sounds and close marked ones, performed here to avoid thread issues
static int32 list_index;
for (list_index = 1; list_index <= snd_handles->indexes; list_index++) {
static snd_struct *snd;
snd = (snd_struct *)list_get(snd_handles, list_index);
if (snd) {
if (snd->close == 2) {
sndclose_now(list_index);
}
} // snd
} // list_index
} // sndsetup
void sub_beep() {
sndsetup();
qb64_generatesound(783.99, 0.2, 0);
sub__delay(0.25);
}
ALCdevice *dev;
ALCcontext *ctx;
struct stat statbuf;
void snd_un_init() {
alcCloseDevice(dev);
return;
}
int32 snd_init_done = 0;
void snd_init() {
if (!snd_init_done) {
dev = alcOpenDevice(NULL);
if (!dev)
goto done;
ctx = alcCreateContext(dev, NULL);
if (!ctx)
goto done;
alcMakeContextCurrent(ctx);
alListener3f(AL_POSITION, 0, 0, 0);
alListener3f(AL_VELOCITY, 0, 0, 0);
alListener3f(AL_ORIENTATION, 0, 0, -1); // facing 'forward' on rhs co-ordinate system
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
}
done:;
snd_init_done = 1;
}
// OPENAL
int32 snd_raw_channel = 0;
int32 func__sndopenraw() {
static int32 handle;
handle = list_add(snd_handles);
static snd_struct *snd;
snd = (snd_struct *)list_get(snd_handles, handle);
snd->internal = 0;
snd->type = 1;
snd->buffer = (uint8 *)malloc(snd_buffer_size);
snd->buffer_size = 0;
snd->stream_buffer_last = 65536;
snd->stream_buffer = (ptrszint *)malloc(sizeof(ptrszint) * (snd->stream_buffer_last + 1)); // range: 1-65536 (0 ignored)
snd->stream_buffer_start = 0;
snd->stream_buffer_next = 1;
alGenSources(1, &snd->al_source);
// if(alGetError()!=AL_NO_ERROR) return 0;
snd->al_buffers = (ALuint *)malloc(sizeof(ALuint) * 4);
alGenBuffers(4, snd->al_buffers);
// if(alGetError()!=AL_NO_ERROR) return 0;
snd->al_buffer_state = (uint8 *)calloc(4, 1);
snd->al_buffer_index = (int32 *)calloc(4, 4);
return handle;
}
void sub__sndraw(float left, float right, int32 handle, int32 passed) {
if (passed & 2) {
if (handle == 0)
return; // note: this would be an invalid handle
} else {
if (!snd_raw_channel)
snd_raw_channel = func__sndopenraw();
handle = snd_raw_channel;
}
static snd_struct *snd;
snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd)
goto error;
if (snd->internal)
goto error;
if (snd->type != 1)
goto error;
while (qb64_sndraw_lock)
Sleep(0);
qb64_sndraw_lock = 1;
if (handle == qb64_internal_sndraw_handle)
qb64_internal_sndraw_lastcall = GetTicks();
static int16 sample_left;
sample_left = left * 32767.0;
static int16 sample_right;
if (passed & 1)
sample_right = right * 32767.0;
else
sample_right = sample_left;
// add sample
if (snd->buffer_size < snd_buffer_size) {
*(int16 *)(snd->buffer + snd->buffer_size) = sample_left;
snd->buffer_size += 2;
*(int16 *)(snd->buffer + snd->buffer_size) = sample_right;
snd->buffer_size += 2;
}
if (snd->buffer_size == snd_buffer_size) {
// detach buffer
static uint8 *buffer;
buffer = snd->buffer;
// create new buffer
snd->buffer = (uint8 *)malloc(snd_buffer_size);
snd->buffer_size = 0;
// attach detached buffer to stream (or discard it)
static int32 p, p2;
p = snd->stream_buffer_next;
p2 = p + 1;
if (p2 > snd->stream_buffer_last)
p2 = 1;
if (p2 == snd->stream_buffer_start) {
free(buffer);
qb64_sndraw_lock = 0;
return;
} // all buffers are full! (quietly ignore this buffer)
snd->stream_buffer[p] = (ptrszint)buffer;
snd->stream_buffer_next = p2;
if (!snd->stream_buffer_start)
snd->stream_buffer_start = 1;
}
qb64_sndraw_lock = 0;
return;
error:
error(5);
return;
}
void snd_mainloop() {
static int64 t;
t = -1;
// scan through all sounds
int32 list_index;
for (list_index = 1; list_index <= snd_handles->indexes; list_index++) {
snd_struct *snd = (snd_struct *)list_get(snd_handles, list_index);
if (!snd)
continue;
if (snd->type == 2) {
if (snd->limit_state == 2) {
if (t == -1)
t = GetTicks();
if (t >= snd->limit_stop_point) {
snd->limit_state = 0;
sub__sndstop(list_index);
}
} // limit_state==2
if (snd->close == 1) {
// directly poll to check the sound's state
ALint al_state;
alGetSourcei(snd->al_seq_left_source, AL_SOURCE_STATE, &al_state);
if (al_state == AL_INITIAL)
snd->state = SND_STATE_STOPPED;
if (al_state == AL_STOPPED)
snd->state = SND_STATE_STOPPED;
if (al_state == AL_PLAYING)
snd->state = SND_STATE_PLAYING;
if (al_state == AL_PAUSED)
snd->state = SND_STATE_PAUSED;
if (snd->state != SND_STATE_PLAYING)
snd->close = 2;
} // snd->close==1
} // 2
if (snd->type == 1) { // RAW
if (snd->close != 2) {
if (snd->stream_buffer_start) {
static int32 repeat;
do {
repeat = 0;
// internal sndraw post padding
// note: without post padding the final, incomplete buffer of sound data would not be played
if (list_index == qb64_internal_sndraw_handle) { // internal sound raw
if (snd->stream_buffer_start == snd->stream_buffer_next) { // on last source buffer
if (snd->buffer_size > 0) { // partial size
if (GetTicks() > (qb64_internal_sndraw_lastcall + 20)) { // no input received for last 0.02 seconds
if (!qb64_sndraw_lock) { // lock (or skip)
qb64_sndraw_lock = 1;
if (qb64_internal_sndraw_postpad) { // post-pad allowed
qb64_internal_sndraw_postpad = 0;
while (snd->buffer_size < snd_buffer_size) {
*(int16 *)(snd->buffer + snd->buffer_size) = 0;
snd->buffer_size += 2;
*(int16 *)(snd->buffer + snd->buffer_size) = 0;
snd->buffer_size += 2;
}
// detach buffer
static uint8 *buffer;
buffer = snd->buffer;
// create new buffer
snd->buffer = (uint8 *)calloc(snd_buffer_size, 1);
snd->buffer_size = 0;
// attach detached buffer to stream (or discard it)
static int32 p, p2;
p = snd->stream_buffer_next;
p2 = p + 1;
if (p2 > snd->stream_buffer_last)
p2 = 1;
if (p2 == snd->stream_buffer_start) {
free(buffer); // all buffers are full! (quietly ignore this buffer)
} else {
snd->stream_buffer[p] = (ptrszint)buffer;
snd->stream_buffer_next = p2;
}
// next sound command to prepad if necessary to begin sound
qb64_internal_sndraw_prepad = 1;
// unlock
qb64_sndraw_lock = 0;
} // post-pad allowed
} // lock (or skip)
} // no input received for last x seconds
} // partial size
} // on last source buffer
} // internal sound raw
if (snd->stream_buffer_start != snd->stream_buffer_next) {
static int32 p, p2;
static int32 i, i2;
p = snd->stream_buffer_start;
p2 = p + 1;
if (p2 > snd->stream_buffer_last)
p2 = 1;
// unqueue processed buffers (if any)
static ALint buffers_processed;
static ALuint buffers[4];
alGetSourcei(snd->al_source, AL_BUFFERS_PROCESSED, &buffers_processed);
if (buffers_processed) {
alSourceUnqueueBuffers(snd->al_source, buffers_processed, &buffers[0]);
// free associated data
for (i2 = 0; i2 < buffers_processed; i2++) {
for (i = 0; i <= 3; i++) {
if (buffers[i2] == snd->al_buffers[i]) {
free((void *)snd->stream_buffer[snd->al_buffer_index[i]]);
snd->al_buffer_state[i] = 2; //"processed"
}
}
}
}
// check for uninitiated buffers
for (i = 0; i <= 3; i++) {
if (snd->al_buffer_state[i] == 0) {
snd->al_buffer_state[i] = 1; //"processing"
snd->al_buffer_index[i] = p;
static ALuint frequency;
static ALenum format;
frequency = snd_frequency;
format = AL_FORMAT_STEREO16;
alBufferData(snd->al_buffers[i], format, (void *)snd->stream_buffer[p], snd_buffer_size, frequency);
alSourceQueueBuffers(snd->al_source, 1, &snd->al_buffers[i]);
static ALint al_state;
alGetSourcei(snd->al_source, AL_SOURCE_STATE, &al_state);
if (al_state != AL_PLAYING) {
alSourcePlay(snd->al_source);
}
goto gotbuffer;
}
}
// check for finished buffers
for (i = 0; i <= 3; i++) {
if (snd->al_buffer_state[i] == 2) { //"processed"
static ALuint buffer;
static ALuint frequency;
static ALenum format;
frequency = snd_frequency;
format = AL_FORMAT_STEREO16;
alBufferData(snd->al_buffers[i], format, (void *)snd->stream_buffer[p], snd_buffer_size, frequency);
alSourceQueueBuffers(snd->al_source, 1, &snd->al_buffers[i]);
static ALint al_state;
alGetSourcei(snd->al_source, AL_SOURCE_STATE, &al_state);
if (al_state != AL_PLAYING) {
alSourcePlay(snd->al_source);
}
snd->al_buffer_index[i] = p;
snd->al_buffer_state[i] = 1; //"processing"
goto gotbuffer;
}
}
i = -1;
gotbuffer:
if (i != -1) {
repeat = 1;
snd->stream_buffer_start = p2;
}
} // queued buffer exists
} while (repeat);
} // started
} // close!=2
// close raw?
if (snd->close == 1) {
if (t == -1)
t = GetTicks();
if (t > (snd->raw_close_time + 3000)) {
static ALint al_state;
alGetSourcei(snd->al_source, AL_SOURCE_STATE, &al_state);
if (al_state == AL_INITIAL)
snd->state = SND_STATE_STOPPED;
if (al_state == AL_STOPPED)
snd->state = SND_STATE_STOPPED;
if (al_state == AL_PLAYING)
snd->state = SND_STATE_PLAYING;
if (al_state == AL_PAUSED)
snd->state = SND_STATE_PAUSED;
if (snd->state != SND_STATE_PLAYING) { // not playing
// note: hardware interface parts closed here, handles closed in sndclose_now
if (snd->al_source) {
alDeleteSources(1, &snd->al_source);
snd->al_source = 0;
}
static int32 i;
for (i = 0; i <= 3; i++) {
if (snd->al_buffers[i])
alDeleteBuffers(1, &snd->al_buffers[i]);
}
// remove the buffers
// 1)remove 4 AL buffers
free(snd->al_buffers);
free(snd->al_buffer_index);
free(snd->al_buffer_state);
// 2)remove build buffer
free(snd->buffer);
// 3)remove the 65536 pointers to potential buffers
free(snd->stream_buffer);
snd->close = 2;
} // not playing
} // time>3 secs
} // sndclose==1
} // RAW
} // list_index loop
}
int32 sndupdate_dont_free_resources = 0;
void sndupdate(snd_struct *snd) {
if (snd->type == 2) { // seq type
static snd_sequence_struct *seq;
seq = snd->seq;
if (snd->al_seq_left_source) {
// update state info
static ALint al_state;
alGetSourcei(snd->al_seq_left_source, AL_SOURCE_STATE, &al_state);
// ref: Each source can be in one of four possible execution states: AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED
if (al_state == AL_INITIAL)
snd->state = SND_STATE_STOPPED;
if (al_state == AL_STOPPED)
snd->state = SND_STATE_STOPPED;
if (al_state == AL_PLAYING)
snd->state = SND_STATE_PLAYING;
if (al_state == AL_PAUSED)
snd->state = SND_STATE_PAUSED;
if (snd->state == SND_STATE_STOPPED) {
if (!sndupdate_dont_free_resources) {
if (snd->setpos_lock_release)
goto no_release;
//###agressively free OpenAL resources (buffers & sources are very limited on some platforms)###
alDeleteSources(1, &snd->al_seq_left_source);
snd->al_seq_left_source = 0;
alDeleteBuffers(1, &snd->al_seq_left_buffer);
snd->al_seq_left_buffer = 0;
if (snd->al_seq_right_source) {
alDeleteSources(1, &snd->al_seq_right_source);
snd->al_seq_right_source = 0;
alDeleteBuffers(1, &snd->al_seq_right_buffer);
snd->al_seq_right_buffer = 0;
}
// flag updates
snd->volume_update = 1;
snd->bal_update = 1;
no_release:;
}
}
if (snd->limit_state == 2) {
if (snd->state != SND_STATE_PLAYING)
snd->limit_state = 0; // disable limit
}
if (snd->al_seq_left_source) { // still valid?
if (snd->bal_update) {
snd->bal_update = 0;
alSource3f(snd->al_seq_left_source, AL_POSITION, snd->bal_left_x, snd->bal_left_y, snd->bal_left_z);
if (snd->al_seq_right_source) {
alSource3f(snd->al_seq_right_source, AL_POSITION, snd->bal_right_x, snd->bal_right_y, snd->bal_right_z);
}
/*
OpenAL -- like OpenGL -- uses a right-handed Cartesian coordinate system (RHS),
where in a frontal default view X (thumb) points right, Y (index finger) points up,
and Z (middle finger) points towards the viewer/camera. To switch from a left handed
coordinate system (LHS) to a right handed coordinate systems, flip the sign on the Z coordinate.
*/
/* ref: old bal system code & notes
x distance values go from left(negative) to right(positive).
y distance values go from below(negative) to above(positive).
z distance values go from behind(negative) to in front(positive).
d=sqrt(x*x+y*y+z*z);
if (d<1) d=0;
if (d>1000) d=1000;
d=1000-d;
d=d/1000.0;
stream_volume_mult1=d;
--------------------
snd[i].posx=x; snd[i].posy=y; snd[i].posz=z;
d=atan2(x,z)*57.295779513;
if (d<0) d=360+d;
i2=d+0.5; if (i2==360) i2=0;//angle
d2=sqrt(x*x+y*y+z*z);//distance
if (d2<1) d2=1;
if (d2>999.9) d2=999.9;
i3=d2/3.90625;
*/
} // snd->bal_update
if (snd->volume_update) {
snd->volume_update = 0;
alSourcef(snd->al_seq_left_source, AL_GAIN, snd->volume);
if (snd->al_seq_right_source) {
alSourcef(snd->al_seq_right_source, AL_GAIN, snd->volume);
}
} // snd->volume_update
if (snd->setpos_lock_release) {
if (snd->state != SND_STATE_STOPPED) {
snd->setpos_lock_release = 0;
}
}
if (snd->setpos_update) {
snd->setpos_update = 0;
alSourcef(snd->al_seq_left_source, AL_SEC_OFFSET, snd->setpos);
if (snd->al_seq_right_source) {
alSourcef(snd->al_seq_right_source, AL_SEC_OFFSET, snd->setpos);
}
snd->setpos_lock_release = 1;
}
} // snd->al_seq_source still valid?
} // snd->al_seq_source
} // 2
/*
if (snd->type==1){
static ALint al_state;
alGetSourcei(snd->al_source,AL_SOURCE_STATE,&al_state);
//ref: Each source can be in one of four possible execution states: AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED
if (al_state==AL_INITIAL) snd->state=SND_STATE_STOPPED;
if (al_state==AL_STOPPED) snd->state=SND_STATE_STOPPED;
if (al_state==AL_PLAYING) snd->state=SND_STATE_PLAYING;
if (al_state==AL_PAUSED) snd->state=SND_STATE_PAUSED;
}
*/
} // sndupdate
int32 func__sndcopy(int32 handle) {
if (new_error)
return 0;
sndsetup();
if (handle == 0)
return 0; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return 0;
}
if (snd->type == 2) {
sndupdate(snd);
// increment seq references
snd->seq->references++;
snd_struct *snd_source = snd;
// create new snd handle
int32 handle = list_add(snd_handles);
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
// import all data
memcpy(snd, snd_source, sizeof(snd_struct));
// adjust data
snd->al_seq_left_buffer = 0; // no buffer
snd->al_seq_left_source = 0; // no source
snd->al_seq_right_buffer = 0;
snd->al_seq_right_source = 0;
snd->volume_update = 1;
snd->state = 0;
return handle;
} // 2
error(5);
return 0;
}
int32 sub__sndvol_error = 0;
void sub__sndvol(int32 handle, float volume) {
if (new_error)
return;
sndsetup();
sub__sndvol_error = 0;
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
sub__sndvol_error = 1;
if (!snd || snd->internal) {
error(5);
return;
}
snd->volume = volume;
snd->volume_update = 1;
sndupdate(snd);
sub__sndvol_error = 0;
}
void sub__sndpause(int32 handle) {
if (new_error)
return;
sndsetup();
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return;
}
if (snd->type == 2) {
sndupdate(snd);
if (snd->al_seq_left_source && snd->state == SND_STATE_PLAYING) {
const ALuint sources[2] = {snd->al_seq_left_source, snd->al_seq_right_source};
alSourcePausev(snd->al_seq_right_source ? 2 : 1, sources);
snd->state = SND_STATE_PAUSED;
if (snd->limit_state == 2) {
snd->limit_state = 0;
}
}
return;
}
error(5);
}
void sub__sndstop(int32 handle) {
if (new_error)
return;
sndsetup();
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return;
}
if (snd->type == 2) {
sndupdate(snd);
if (snd->al_seq_left_source && snd->state != SND_STATE_STOPPED) {
const ALuint sources[2] = {snd->al_seq_left_source, snd->al_seq_right_source};
alSourceStopv(snd->al_seq_right_source ? 2 : 1, sources);
if (snd->limit_state == 2) {
snd->limit_state = 0; // disable limit
}
}
return;
} // 2
error(5);
}
int32 sndplay_loop = 0;
void sub__sndplay(int32 handle) {
if (new_error)
return;
sndsetup();
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd) {
error(5);
return;
}
if (!snd_allow_internal) {
if (snd->internal) {
error(5);
return;
}
}
if (snd->type == 2) {
snd_sequence_struct *seq;
seq = snd->seq;
if (!snd->al_seq_left_buffer) {
alGenBuffers(1, &snd->al_seq_left_buffer);
if (!snd->al_seq_right_buffer && seq->data_right) {
alGenBuffers(1, &snd->al_seq_right_buffer);
}
snd->al_seq_freq = snd_frequency;
snd->al_seq_format = AL_FORMAT_MONO16;
alBufferData(snd->al_seq_left_buffer, snd->al_seq_format, seq->data_left, seq->data_left_size, snd_frequency);
if (seq->data_right) {
alBufferData(snd->al_seq_right_buffer, snd->al_seq_format, seq->data_right, seq->data_right_size, snd_frequency);
}
}
if (!snd->al_seq_left_source) {
alGenSources(1, &snd->al_seq_left_source);
alSourcef(snd->al_seq_left_source, AL_REFERENCE_DISTANCE, 0.01);
alSourcef(snd->al_seq_left_source, AL_MAX_DISTANCE, 10);
alSourcef(snd->al_seq_left_source, AL_ROLLOFF_FACTOR, 1);
alSourcei(snd->al_seq_left_source, AL_BUFFER, snd->al_seq_left_buffer);
}
if (!snd->al_seq_right_source && seq->data_right) {
alGenSources(1, &snd->al_seq_right_source);
alSourcef(snd->al_seq_right_source, AL_REFERENCE_DISTANCE, 0.01);
alSourcef(snd->al_seq_right_source, AL_MAX_DISTANCE, 10);
alSourcef(snd->al_seq_right_source, AL_ROLLOFF_FACTOR, 1);
alSourcei(snd->al_seq_right_source, AL_BUFFER, snd->al_seq_right_buffer);
}
sndupdate_dont_free_resources = 1;
sndupdate(snd);
if (snd->state == SND_STATE_PLAYING) {
sub__sndstop(handle);
sndupdate(snd);
}
sndupdate_dont_free_resources = 0;
alSourcei(snd->al_seq_left_source, AL_LOOPING, sndplay_loop ? AL_TRUE : AL_FALSE);
alSourcei(snd->al_seq_right_source, AL_LOOPING, sndplay_loop ? AL_TRUE : AL_FALSE);
const ALuint sources[2] = {snd->al_seq_left_source, snd->al_seq_right_source};
alSourcePlayv(snd->al_seq_right_source ? 2 : 1, sources);
snd->state = SND_STATE_PLAYING;
if (snd->limit_state == 2)
snd->limit_state = 0; // disable limit
if (snd->limit_state == 1) {
snd->limit_stop_point = GetTicks() + (snd->limit_duration * 1000.0);
snd->limit_state = 2;
}
return;
} // 2
error(5);
} // sndplay
void sub__sndloop(int32 handle) {
sndplay_loop = 1;
sub__sndplay(handle); // call SNDPLAY with loop option set
sndplay_loop = 0;
} // sndloop
int32 func__sndpaused(int32 handle) {
if (new_error)
return 0;
sndsetup();
if (handle == 0)
return 0; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return 0;
}
sndupdate(snd);
if (snd->state == SND_STATE_PAUSED)
return -1;
return 0;
}
int32 func__sndplaying(int32 handle) {
if (new_error)
return 0;
sndsetup();
if (handle == 0)
return 0; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return 0;
}
sndupdate(snd);
if (snd->state == SND_STATE_PLAYING)
return -1;
return 0;
}
double func__sndlen(int32 handle) {
if (new_error)
return 0;
sndsetup();
if (handle == 0)
return 0; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return 0;
}
if (snd->type == 2) {
snd_sequence_struct *seq = snd->seq;
int32 samples = seq->data_size / seq->channels / (seq->bits_per_sample / 8);
double seconds = samples / (double)seq->sample_rate;
return seconds;
} // 2
error(5);
return 0;
}
void sub__sndbal(int32 handle, double x, double y, double z, int32 channel, int32 passed) {
if (new_error)
return;
sndsetup();
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal || channel < 1 || channel > 2) {
error(5);
return;
}
if (snd->type != 2)
return;
if (!(passed & 8)) {
channel = 1;
}
if (channel == 1) {
if (passed & 1)
snd->bal_left_x = x / 100;
if (passed & 2)
snd->bal_left_y = y / 100;
if (passed & 4)
snd->bal_left_z = z / 100;
} else if (channel == 2) {
if (passed & 1)
snd->bal_right_x = x / 100;
if (passed & 2)
snd->bal_right_y = y / 100;
if (passed & 4)
snd->bal_right_z = z / 100;
}
snd->bal_update = 1;
sndupdate(snd);
}
double func__sndgetpos(int32 handle) {
if (new_error)
return 0;
sndsetup();
if (handle == 0)
return 0; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return 0;
}
if (snd->type == 2) {
if (snd->al_seq_left_source) {
float seconds;
alGetSourcef(snd->al_seq_left_source, AL_SEC_OFFSET, &seconds);
return seconds;
}
return 0;
}
error(5);
return 0;
} // getpos
void sub__sndsetpos(int32 handle, double seconds) {
if (new_error)
return;
sndsetup();
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal || seconds < 0) {
error(5);
return;
}
if (snd->type == 2) {
snd->setpos = seconds;
snd->setpos_update = 1;
sndupdate(snd);
return;
}
error(5);
} // setpos
void sub__sndlimit(int32 handle, double limit) {
if (new_error)
return;
sndsetup();
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal || limit < 0) {
error(5);
return;
}
if (snd->type == 2) {
if (limit == 0) {
snd->limit_state = 0;
return;
}
sndupdate(snd);
if (snd->state == SND_STATE_PLAYING) {
// begin count immediately
snd->limit_stop_point = GetTicks() + (limit * 1000.0);
snd->limit_state = 2;
} else {
// begin after play is called
snd->limit_duration = limit;
snd->limit_state = 1;
}
return;
}
error(5);
}
// note: this is an internal command
void sndclose_now(int32 handle) {
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (snd->type == 2) {
// remove OpenAL content
if (snd->al_seq_left_source) {
alDeleteSources(1, &snd->al_seq_left_source);
snd->al_seq_left_source = 0;
alDeleteBuffers(1, &snd->al_seq_left_buffer);
snd->al_seq_left_buffer = 0;
}
if (snd->al_seq_right_source) {
alDeleteSources(1, &snd->al_seq_right_source);
snd->al_seq_right_source = 0;
alDeleteBuffers(1, &snd->al_seq_right_buffer);
snd->al_seq_right_buffer = 0;
}
// remove sound handle
list_remove(snd_handles, handle);
// remove seq
if (snd->seq->references > 1) {
snd->seq->references--;
} else {
if (snd->seq->data)
free(snd->seq->data);
if (snd->seq->data_left != snd->seq->data)
free(snd->seq->data_left);
if (snd->seq->data_right)
free(snd->seq->data_right);
list_remove(snd_sequences, list_get_index(snd_sequences, snd->seq));
}
} // 2
if (snd->type == 1) {
list_remove(snd_handles, handle);
}
} // sndclose_now
void sub__sndclose(int32 handle) {
if (new_error)
return;
sndsetup();
if (handle == 0)
return; // default response
snd_struct *snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd || snd->internal) {
error(5);
return;
}
sndupdate(snd);
snd->internal = 1; // switch to internal, no more commands related to this sound can be accepted
if (snd->type == 2) {
if (snd->state == SND_STATE_PLAYING) {
snd->close = 1; // close when finished playing
} else {
sndclose_now(handle);
}
return;
}
snd->close = 1; // raw
snd->raw_close_time = GetTicks();
if (snd->lock_id) {
free_mem_lock((mem_lock *)snd->lock_offset); // untag
}
} // sndclose
//"macros"
void sub__sndplaycopy(int32 handle, double volume, int32 passed) {
if (new_error)
return;
sndsetup();
int32 handle2;
handle2 = func__sndcopy(handle);
if (!handle2)
return; // an error has already happened
if (passed) {
sub__sndvol(handle2, volume);
if (sub__sndvol_error) {
sub__sndclose(handle2);
return;
}
}
sub__sndplay(handle2);
sub__sndclose(handle2);
}
// uint8 *soundwave(double frequency,double length,double volume,double fadein,double fadeout,uint8 *data);
// uint8 *soundwavesilence(double length,uint8 *data);
int32 func_play(int32 ignore) { return 0; }
/*
Formats:
A[#|+|-][0-64]
0-64 is like temp. Lnumber, 0 is whatever the current default is
*/
void sub_play(qbs *str) {
sndsetup();
static uint8 *b, *wave, *wave2, *wave3;
static double d;
static int32 i, bytes_left, a, x, x2, x3, x4, x5, wave_bytes, wave_base;
static int32 o = 4;
static double t = 120; // quarter notes per minute (120/60=2 per second)
static double l = 4;
static double pause = 1.0 / 8.0; // ML 0.0, MN 1.0/8.0, MS 1.0/4.0
static double length, length2; // derived from l and t
static double frequency;
static double mb = 0;
static double v = 50;
static int32 n; // the semitone-intervaled note to be played
static int32 n_changed; //+,#,- applied?
static int64 number;
static int32 number_entered;
static int32 followup; // 1=play note
static int32 playit;
static uint32 handle = NULL;
static int32 fullstops = 0;
b = str->chr;
bytes_left = str->len;
wave = NULL;
wave_bytes = 0;
n_changed = 0;
n = 0;
number_entered = 0;
number = 0;
followup = 0;
length = 1.0 / (t / 60.0) * (4.0 / l);
playit = 0;
wave_base = 0; // point at which new sounds will be inserted
next_byte:
if ((bytes_left--) || followup) {
if (bytes_left < 0) {
i = 32;
goto follow_up;
}
i = *b++;
if (i == 32)
goto next_byte;
if (i >= 97 && i <= 122)
a = i - 32;
else
a = i;
if (i == 61) { //= (+VARPTR$)
if (fullstops) {
error(5);
return;
}
if (number_entered) {
error(5);
return;
}
number_entered = 2;
// VARPTR$ reference
/*
'BYTE=1
'INTEGER=2
'STRING=3 SUB-STRINGS must use "X"+VARPTR$(string$)
'SINGLE=4
'INT64=5
'FLOAT=6
'DOUBLE=8
'LONG=20
'BIT=64+n
*/
if (bytes_left < 3) {
error(5);
return;
}
i = *b++;
bytes_left--; // read type byte
x = *(uint16 *)b;
b += 2;
bytes_left -= 2; // read offset within DBLOCK
// note: allowable _BIT type variables in VARPTR$ are all at a byte offset and are all
// padded until the next byte
d = 0;
switch (i) {
case 1:
d = *(char *)(dblock + x);
break;
case (1 + 128):
d = *(uint8 *)(dblock + x);
break;
case 2:
d = *(int16 *)(dblock + x);
break;
case (2 + 128):
d = *(uint16 *)(dblock + x);
break;
case 4:
d = *(float *)(dblock + x);
break;
case 5:
d = *(int64 *)(dblock + x);
break;
case (5 + 128):
d = *(int64 *)(dblock + x); // unsigned conversion is unsupported!
break;
case 6:
d = *(long double *)(dblock + x);
break;
case 8:
d = *(double *)(dblock + x);
break;
case 20:
d = *(int32 *)(dblock + x);
break;
case (20 + 128):
d = *(uint32 *)(dblock + x);
break;
default:
// bit type?
if ((i & 64) == 0) {
error(5);
return;
}
x2 = i & 63;
if (x2 > 56) {
error(5);
return;
} // valid number of bits?
// create a mask
static int64 i64num, mask, i64x;
mask = (((int64)1) << x2) - 1;
i64num = (*(int64 *)(dblock + x)) & mask;
// signed?
if (i & 128) {
mask = ((int64)1) << (x2 - 1);
if (i64num & mask) { // top bit on?
mask = -1;
mask <<= x2;
i64num += mask;
}
} // signed
d = i64num;
}
if (d > 2147483647.0 || d < -2147483648.0) {
error(5);
return;
} // out of range value!
number = qbr_double_to_long(d);
goto next_byte;
}
// read in a number
if ((i >= 48) && (i <= 57)) {
if (fullstops || (number_entered == 2)) {
error(5);
return;
}
if (!number_entered) {
number = 0;
number_entered = 1;
}
number = number * 10 + i - 48;
goto next_byte;
}
// read fullstops
if (i == 46) {
if (followup != 7 && followup != 1 && followup != 4) {
error(5);
return;
}
fullstops++;
goto next_byte;
}
follow_up:
if (followup == 8) { // V...
if (!number_entered) {
error(5);
return;
}
number_entered = 0;
if (number > 100) {
error(5);
return;
}
v = number;
followup = 0;
if (bytes_left < 0)
goto done;
} // 8
if (followup == 7) { // P...
if (number_entered) {
number_entered = 0;
if (number < 1 || number > 64) {
error(5);
return;
}
length2 = 1.0 / (t / 60.0) * (4.0 / ((double)number));
} else {
length2 = length;
}
d = length2;
for (x = 1; x <= fullstops; x++) {
d /= 2.0;
length2 = length2 + d;
}
fullstops = 0;
soundwave_bytes = wavesize(length2);
if (!wave) {
// create buffer
wave = (uint8 *)calloc(soundwave_bytes, 1);
wave_bytes = soundwave_bytes;
wave_base = 0;
} else {
// increase buffer?
if ((wave_base + soundwave_bytes) > wave_bytes) {
wave = (uint8 *)realloc(wave, wave_base + soundwave_bytes);
memset(wave + wave_base, 0, wave_base + soundwave_bytes - wave_bytes);
wave_bytes = wave_base + soundwave_bytes;
}
}
if (i != 44) {
wave_base += soundwave_bytes;
}
playit = 1;
followup = 0;
if (i == 44)
goto next_byte;
if (bytes_left < 0)
goto done;
} // 7
if (followup == 6) { // T...
if (!number_entered) {
error(5);
return;
}
number_entered = 0;
if (number < 32 || number > 255) {
number = 120;
}
t = number;
length = 1.0 / (t / 60.0) * (4.0 / l);
followup = 0;
if (bytes_left < 0)
goto done;
} // 6
if (followup == 5) { // M...
if (number_entered) {
error(5);
return;
}
switch (a) {
case 76: // L
pause = 0;
break;
case 78: // N
pause = 1.0 / 8.0;
break;
case 83: // S
pause = 1.0 / 4.0;
break;
case 66: // MB
if (!mb) {
mb = 1;
if (playit) {
playit = 0;
qb64_internal_sndraw(wave, wave_bytes, 1);
}
wave = NULL;
}
break;
case 70: // MF
if (mb) {
mb = 0;
// preceding MB content incorporated into MF block
}
break;
default:
error(5);
return;
}
followup = 0;
goto next_byte;
} // 5
if (followup == 4) { // N...
if (!number_entered) {
error(5);
return;
}
number_entered = 0;
if (number > 84) {
error(5);
return;
}
n = -33 + number;
goto followup1;
followup = 0;
if (bytes_left < 0)
goto done;
} // 4
if (followup == 3) { // O...
if (!number_entered) {
error(5);
return;
}
number_entered = 0;
if (number > 6) {
error(5);
return;
}
o = number;
followup = 0;
if (bytes_left < 0)
goto done;
} // 3
if (followup == 2) { // L...
if (!number_entered) {
error(5);
return;
}
number_entered = 0;
if (number < 1 || number > 64) {
error(5);
return;
}
l = number;
length = 1.0 / (t / 60.0) * (4.0 / l);
followup = 0;
if (bytes_left < 0)
goto done;
} // 2
if (followup == 1) { // A-G...
if (i == 45) { //-
if (n_changed || number_entered) {
error(5);
return;
}
n_changed = 1;
n--;
goto next_byte;
}
if (i == 43 || i == 35) { //+,#
if (n_changed || number_entered) {
error(5);
return;
}
n_changed = 1;
n++;
goto next_byte;
}
followup1:
if (number_entered) {
number_entered = 0;
if (number < 0 || number > 64) {
error(5);
return;
}
if (!number)
length2 = length;
else
length2 = 1.0 / (t / 60.0) * (4.0 / ((double)number));
} else {
length2 = length;
} // number_entered
d = length2;
for (x = 1; x <= fullstops; x++) {
d /= 2.0;
length2 = length2 + d;
}
fullstops = 0;
// frequency=(2^(note/12))*440
frequency = pow(2.0, ((double)n) / 12.0) * 440.0;
// create wave
wave2 = soundwave(frequency, length2 * (1.0 - pause), v / 100.0, NULL, NULL);
if (pause > 0) {
wave2 = (uint8 *)realloc(wave2, soundwave_bytes + wavesize(length2 * pause));
memset(wave2 + soundwave_bytes, 0, wavesize(length2 * pause));
soundwave_bytes += wavesize(length2 * pause);
}
if (!wave) {
// adopt buffer
wave = wave2;
wave_bytes = soundwave_bytes;
wave_base = 0;
} else {
// mix required?
if (wave_base == wave_bytes)
x = 0;
else
x = 1;
// increase buffer?
if ((wave_base + soundwave_bytes) > wave_bytes) {
wave = (uint8 *)realloc(wave, wave_base + soundwave_bytes);
memset(wave + wave_base, 0, wave_base + soundwave_bytes - wave_bytes);
wave_bytes = wave_base + soundwave_bytes;
}
// mix or copy
if (x) {
// mix
static int16 *sp, *sp2;
sp = (int16 *)(wave + wave_base);
sp2 = (int16 *)wave2;
x2 = soundwave_bytes / 2;
for (x = 0; x < x2; x++) {
x3 = *sp2++;
x4 = *sp;
x4 += x3;
if (x4 > 32767)
x4 = 32767;
if (x4 < -32767)
x4 = -32767;
*sp++ = x4;
} // x
} else {
// copy
memcpy(wave + wave_base, wave2, soundwave_bytes);
} // x
free(wave2);
}
if (i != 44) {
wave_base += soundwave_bytes;
}
playit = 1;
n_changed = 0;
followup = 0;
if (i == 44)
goto next_byte;
if (bytes_left < 0)
goto done;
} // 1
if (a >= 65 && a <= 71) {
// modify a to represent a semitonal note (n) interval
switch (a) {
//[c][ ][d][ ][e][f][ ][g][ ][a][ ][b]
// 0 1 2 3 4 5 6 7 8 9 0 1
case 65:
n = 9;
break;
case 66:
n = 11;
break;
case 67:
n = 0;
break;
case 68:
n = 2;
break;
case 69:
n = 4;
break;
case 70:
n = 5;
break;
case 71:
n = 7;
break;
}
n = n + (o - 2) * 12 - 9;
followup = 1;
goto next_byte;
} // a
if (a == 76) { // L
followup = 2;
goto next_byte;
}
if (a == 77) { // M
followup = 5;
goto next_byte;
}
if (a == 78) { // N
followup = 4;
goto next_byte;
}
if (a == 79) { // O
followup = 3;
goto next_byte;
}
if (a == 84) { // T
followup = 6;
goto next_byte;
}
if (a == 60) { //<
o--;
if (o < 0)
o = 0;
goto next_byte;
}
if (a == 62) { //>
o++;
if (o > 6)
o = 6;
goto next_byte;
}
if (a == 80) { // P
followup = 7;
goto next_byte;
}
if (a == 86) { // V
followup = 8;
goto next_byte;
}
error(5);
return;
} // bytes_left
done:
if (number_entered || followup) {
error(5);
return;
} // unhandled data
if (playit) {
if (mb) {
qb64_internal_sndraw(wave, wave_bytes, 0);
} else {
qb64_internal_sndraw(wave, wave_bytes, 1);
}
} // playit
}
int32 func__sndrate() { return snd_frequency; }
void qb64_generatesound(double f, double l, uint8 block) {
sndsetup();
static uint8 *data;
data = soundwave(f, l, 1, 0, 0);
qb64_internal_sndraw(data, soundwave_bytes, block);
}
void qb64_internal_sndraw(uint8 *data, int32 bytes, int32 block) { // data required in 16bit stereo at native frequency, data is freed
sndsetup();
static int32 i;
if (qb64_internal_sndraw_handle == 0) {
qb64_internal_sndraw_handle = func__sndopenraw();
qb64_internal_sndraw_prepad = 1;
}
int64 buffered_ms;
if (block) {
buffered_ms = func__sndrawlen(qb64_internal_sndraw_handle, 1) * 1000.0;
buffered_ms = ((double)buffered_ms) * 0.95; // take 95% of actual length to allow time for processing of new content
buffered_ms -= 250; // allow for latency (call frequency and pre/post pad)
if (buffered_ms < 0)
buffered_ms = 0;
}
if (qb64_internal_sndraw_prepad) {
qb64_internal_sndraw_prepad = 0;
// pad initial buffer so that first sound is played immediately
static int32 snd_buffer_size_samples;
snd_buffer_size_samples = snd_buffer_size / 2 / 2;
static int32 n;
n = snd_buffer_size_samples - (bytes / 2 / 2);
if (n > 0) {
for (i = 0; i < n; i++) {
sub__sndraw(0, 0, qb64_internal_sndraw_handle, 2);
}
}
}
// move data into sndraw handle
for (i = 0; i < bytes; i += 4) {
sub__sndraw((float)((int16 *)(data + i))[0] / 32768.0, (float)((int16 *)(data + i))[1] / 32768.0, qb64_internal_sndraw_handle, 1 + 2);
}
qb64_internal_sndraw_postpad = 1;
free(data); // free the sound data
if (block) {
int64 length_ms;
length_ms = (((bytes / 2 / 2) * 1000) / snd_frequency); // length in ms
length_ms = ((double)length_ms) * 0.95; // take 95% of actual length to allow time for processing of new content
length_ms -= 250; // allow for latency (call frequency and pre/post pad)
if (length_ms > 0) {
sub__delay(((double)length_ms + (double)buffered_ms) / 1000.0);
}
}
}
double func__sndrawlen(int32 handle, int32 passed) {
if (passed) {
if (handle == 0)
return 0;
} else {
if (!snd_raw_channel)
return 0;
handle = snd_raw_channel;
}
static snd_struct *snd;
snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd)
goto error;
if (snd->internal)
goto error;
if (snd->type != 1)
goto error;
if (!snd->stream_buffer_start)
return 0;
// count buffered source buffers
static int32 source_buffers;
source_buffers = 0;
static int32 i;
i = snd->stream_buffer_start;
while (i != snd->stream_buffer_next) {
source_buffers++;
i++;
if (i > snd->stream_buffer_last)
i = 1;
}
// count dest buffers
static int32 dest_buffers;
dest_buffers = 0;
for (i = 0; i <= 3; i++) {
if (snd->al_buffer_state[i] == 1)
dest_buffers++;
}
static double result;
result = ((double)((dest_buffers + source_buffers) * (snd_buffer_size / 2 / 2))) / (double)snd_frequency;
if (result < .375)
result = 0; // hack to reenable _SNDRAWLEN, which gets stuck at .3715192763764172
return result;
error:
error(5);
return 0;
}
void sub__sndrawdone(int32 handle, int32 passed) {
if (passed) {
if (handle == 0)
return;
} else {
if (!snd_raw_channel)
return;
handle = snd_raw_channel;
}
static snd_struct *snd;
snd = (snd_struct *)list_get(snd_handles, handle);
if (!snd)
goto error;
if (snd->internal)
goto error;
if (snd->type != 1)
goto error;
if (snd->buffer_size > 0) { // partial size
if (!qb64_sndraw_lock) { // lock (or skip)
qb64_sndraw_lock = 1;
while (snd->buffer_size < snd_buffer_size) {
*(int16 *)(snd->buffer + snd->buffer_size) = 0;
snd->buffer_size += 2;
*(int16 *)(snd->buffer + snd->buffer_size) = 0;
snd->buffer_size += 2;
}
// detach buffer
static uint8 *buffer;
buffer = snd->buffer;
// create new buffer
snd->buffer = (uint8 *)calloc(snd_buffer_size, 1);
snd->buffer_size = 0;
// attach detached buffer to stream (or discard it)
static int32 p, p2;
p = snd->stream_buffer_next;
p2 = p + 1;
if (p2 > snd->stream_buffer_last)
p2 = 1;
if (p2 == snd->stream_buffer_start) {
free(buffer); // all buffers are full! (quietly ignore this buffer)
} else {
snd->stream_buffer[p] = (ptrszint)buffer;
snd->stream_buffer_next = p2;
if (!snd->stream_buffer_start)
snd->stream_buffer_start = 1;
}
// unlock
qb64_sndraw_lock = 0;
} // lock (or skip)
} // partial size
return;
error:
error(5);
return;
}
#endif