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

1856 lines
No EOL
45 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 <sys\\types.h>
#include <sys\\stat.h>
#include <unistd.h>
#include <stdlib.h>
#else
#include "AL/al.h"
#include "AL/alc.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.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{
uint8 *data;
int32 data_size;
uint8 channels;//note: more than 2 channels may be supported in the future
uint8 *data_stereo;//note: built/linked-to when available/required, otherwise NULL
int32 data_stereo_size;
uint8 *data_mono;//note: built/linked-to when available/required, otherwise NULL
int32 data_mono_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{
uint8 internal;//1=internal
uint8 type;//1=RAW, 2=SEQUENCE
uint8 streamed;//1(NOT SYNC) or 0(SYNC)
//sequence
snd_sequence_struct *seq;
//----part specific variables----
ALuint al_seq_buffer;
ALenum al_seq_format;
ALsizei al_seq_size;
ALsizei al_seq_freq;
ALboolean al_seq_loop;
ALvoid* al_seq_data;
ALuint al_seq_source;
//-------------------------------
float volume;
uint8 volume_update;
//----part specific variables----
float al_volume;
//-------------------------------
uint8 use_mono;
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_x,bal_y,bal_z;
uint8 bal_update;
//----part specific variables----
float al_bal_x,al_bal_y,al_bal_z;
//-------------------------------
//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 capability;
uint8 state;
//old stuff below...sigh!
uint8 free;
uint8 playing;
uint8 paused;
float volume_mult1;
float posx;
float posy;
float posz;
uint32 start_time;
uint32 paused_time;
uint8 looping;
uint32 limit;
double len;
};
#define SND_STATE_STOPPED 0
#define SND_STATE_PLAYING 1
#define SND_STATE_PAUSED 2
list *snd_handles=list_new(sizeof(snd_struct));
int32 snd_stream_handle=0;//only one sound can be streamed
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) exit(111);
ctx = alcCreateContext(dev, NULL);
alcMakeContextCurrent(ctx); if(!ctx) exit(222);
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);
}
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
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->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
static ALint al_state;
alGetSourcei(snd->al_seq_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
}//snd
}//list_index loop
}
#define SND_CAPABILITY_SYNC 1
#define SND_CAPABILITY_VOL 2
#define SND_CAPABILITY_PAUSE 4
#define SND_CAPABILITY_LEN 8
#define SND_CAPABILITY_SETPOS 16
/*
struct snd_struct{
uint32 handle;
Mix_Chunk *sample;
uint8 free;
uint8 playing;
uint8 paused;
float volume;
float volume_mult1;
float posx;
float posy;
float posz;
uint32 start_time;
uint32 paused_time;
uint8 looping;
uint32 limit;
double len;
uint8 capability;
};
snd_struct snd[AUDIO_CHANNELS];
Mix_Music *stream=NULL;
int32 stream_free=0;
int32 stream_loaded=0;
int32 stream_playing=0;
uint32 snd_free_handle=2;
int32 stream_type=0;
int32 stream_paused=0;
float stream_volume=1;
float stream_volume_mult1=1;
uint32 stream_start_time=0;
uint32 stream_paused_time=0;
double stream_setpos=0;
int32 stream_looping=0;
double stream_limit=0;
int32 stream_limited=0;
double stream_len=-1;
uint8 stream_capability;
void snd_check(){
sndsetup();
static int32 i,i2,i3;
//check stream
if (stream_loaded){
if (stream_free){
//still playing?
if (stream_playing&&(!stream_looping)) if (!Mix_PlayingMusic()) stream_playing=0;
if (!stream_playing){
Mix_FreeMusic(stream);
stream=NULL;
}
}
}
//check samples
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle){
if (snd[i].free){
//still playing?
if (snd[i].playing&&(!snd[i].looping)) if (!Mix_Playing(i)) snd[i].playing=0;
if (!snd[i].playing){
snd[i].handle=0;
//free sample data if unrequired by any other sample
i3=1;
for (i2=0;i2<AUDIO_CHANNELS;i2++){
if (snd[i2].handle){
if (snd[i2].sample==snd[i].sample){i3=0; break;}
}}
if (i3) Mix_FreeChunk(snd[i].sample);
}//!stream_playing
}//free
}//handle
}//i
}//snd_check
uint32 func__sndraw(uint8* data,uint32 bytes){
sndsetup();
//***unavailable to QB64 user***
static int32 i,i2,i3;
//find available index
for (i=0;i<AUDIO_CHANNELS;i++){
if (snd[i].handle==0){
memset(&snd[i],0,sizeof(snd_struct));//clear structure
Mix_Volume(i,128);//restore full volume
snd[i].volume=1;
snd[i].volume_mult1=1;
snd[i].sample=Mix_QuickLoad_RAW((Uint8*)data,bytes);
//length_in_sec=size_in_bytes/samples_per_sec/bytes_per_sample/channels
snd[i].len=((double)snd[i].sample->alen)/22050.0/2.0/2.0;
snd[i].handle=snd_free_handle; snd_free_handle++; if (snd_free_handle==0) snd_free_handle=2;
snd[i].capability=SND_CAPABILITY_SYNC;
return snd[i].handle;
}
}
return 0;//no free indexes
}
*/
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_source){
//update state info
static ALint al_state;
alGetSourcei(snd->al_seq_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_source); snd->al_seq_source=0;
alDeleteBuffers(1,&snd->al_seq_buffer); snd->al_seq_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_source){//still valid?
if (snd->bal_update){
snd->bal_update=0;
if (snd->bal_y||snd->bal_z){
snd->use_mono=1;
}
if ((!snd->use_mono)||(!seq->data_mono)){//2D repositioning
if (seq->data_stereo){
//note: OpenAL would ignore our call so use a volume change to simulate distance
static double d;
d=sqrt(snd->bal_x*snd->bal_x+snd->bal_y*snd->bal_y+snd->bal_z*snd->bal_z);
if (d<1) d=0;
if (d>1000) d=1000;
d=1000-d;
d=d/1000.0;//'d' is now a scaling factor from 0 to 1
d=d*((double)snd->volume);
alSourcef(snd->al_seq_source,AL_GAIN,d);
snd->volume_update=0;//cancel possible volume update
goto skip_bal_change;
}
}
//rescale our audio co-ordinate system (-1000[-1] to [1]1000) to match OpenAL(-10[-0.01] to [0.01]10)
snd->al_bal_x=snd->bal_x/100.0;
snd->al_bal_y=snd->bal_y/100.0;
snd->al_bal_z=-(snd->bal_z/100.0);
alSource3f(snd->al_seq_source, AL_POSITION, snd->al_bal_x, snd->al_bal_y, snd->al_bal_z);
skip_bal_change:;
/*
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;
snd->al_volume=snd->volume;
if (snd->bal_x&&((!snd->use_mono)||(!seq->data_mono))){
if (seq->data_stereo){
static double d;
d=sqrt(snd->al_bal_x*snd->al_bal_x+snd->al_bal_y*snd->al_bal_y+snd->al_bal_z*snd->al_bal_z);
if (d<1) d=0;
if (d>1000) d=1000;
d=1000-d;
d=d/1000.0;//'d' is now a scaling factor from 0 to 1
d=d*((double)snd->volume);
alSourcef(snd->al_seq_source,AL_GAIN,d);
goto skip_regular_vol_change;
}
}
alSourcef(snd->al_seq_source,AL_GAIN,snd->al_volume);
skip_regular_vol_change:;
}//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_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
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return 0;}
if (snd->internal){error(5); return 0;}
if (snd->type==2){
if (snd->streamed){error(5); return 0;}
sndupdate(snd);
static snd_sequence_struct *seq; seq=snd->seq;
//increment seq references
seq->references++;
static snd_struct *snd_source; snd_source=snd;
//create new snd handle
static int32 handle; handle=list_add(snd_handles);
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
//import all data
memcpy (snd,snd_source,sizeof(snd_struct));
//adjust data
snd->al_seq_buffer=0;//no buffer
snd->al_seq_source=0;//no source
snd->volume_update=1;
snd->state=0;
return handle;
}//2
error(5); return 0;
}//sndcopy
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
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
sub__sndvol_error=1;
if (!snd){error(5); return;}
if (snd->internal){error(5); return;}
if (!(snd->capability&SND_CAPABILITY_VOL)){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
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return;}
if (!(snd->capability&SND_CAPABILITY_PAUSE)){error(5); return;}
if (snd->internal){error(5); return;}
if (snd->type==2){
static snd_sequence_struct *seq; seq=snd->seq;
sndupdate(snd);
if (snd->al_seq_source){
if (snd->state==SND_STATE_PLAYING){
alSourcePause(snd->al_seq_source); snd->state=SND_STATE_PAUSED;
if (snd->limit_state==2) snd->limit_state=0;//disable limit
}
}//source
return;
}//2
error(5);
}//sndpause
void sub__sndstop(int32 handle){
if (new_error) return;
sndsetup();
if (handle==0) return;//default response
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return;}
if (snd->internal){error(5); return;}
if (snd->type==2){
static snd_sequence_struct *seq; seq=snd->seq;
sndupdate(snd);
if (snd->al_seq_source){
if (snd->state!=SND_STATE_STOPPED){
alSourceStop(snd->al_seq_source); snd->state=SND_STATE_STOPPED;
if (snd->limit_state==2) snd->limit_state=0;//disable limit
}
}//source
return;
}//2
error(5);
}//sndstop
void snd_seq_buildmono(snd_sequence_struct* seq){
if (!seq->data_mono){
//we know: its stereo, its 16-bit
//our plan: average out every 2nd sample
static int32 samples;
samples=seq->data_stereo_size/4;
static int16 *src;
static int16 *dst;
src=(int16*)seq->data_stereo;
dst=(int16*)malloc(samples*2);
static int32 i;
for (i=0;i<samples;i++){
dst[i]=(src[i*2]+src[i*2+1])/2;
}//i
seq->data_mono=(uint8*)dst;
seq->data_mono_size=samples*2;
}//no mono
}//snd_seq_buildmono
int32 sndplay_loop=0;
void sub__sndplay(int32 handle){
if (new_error) return;
sndsetup();
if (handle==0) return;//default response
static snd_struct *snd; 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){
static snd_sequence_struct *seq; seq=snd->seq;
switch_to_mono:
if (!snd->al_seq_buffer){
alGenBuffers(1,&snd->al_seq_buffer);
snd->al_seq_freq=snd_frequency;
static int32 mono;
mono=0;
if (!seq->data_stereo) mono=1;
if (seq->data_mono) mono=1;
if (mono){
snd->al_seq_data=(ALvoid*)seq->data_mono;
snd->al_seq_size=seq->data_mono_size;
snd->al_seq_format=AL_FORMAT_MONO16;
}else{
snd->al_seq_data=(ALvoid*)seq->data_stereo;
snd->al_seq_size=seq->data_stereo_size;
snd->al_seq_format=AL_FORMAT_STEREO16;
}
/* ref:
#define AL_FORMAT_MONO8 0x1100
#define AL_FORMAT_MONO16 0x1101
#define AL_FORMAT_STEREO8 0x1102
#define AL_FORMAT_STEREO16 0x1103
*/
alBufferData(snd->al_seq_buffer,snd->al_seq_format,snd->al_seq_data,snd->al_seq_size,snd->al_seq_freq);
}
if (!snd->al_seq_source){
alGenSources(1,&snd->al_seq_source);
alSourcef(snd->al_seq_source,AL_REFERENCE_DISTANCE,0.01);
alSourcef(snd->al_seq_source,AL_MAX_DISTANCE,10);
alSourcef(snd->al_seq_source,AL_ROLLOFF_FACTOR,1);
alSourcei(snd->al_seq_source,AL_BUFFER,snd->al_seq_buffer);
}
sndupdate_dont_free_resources=1;
sndupdate(snd);
if (snd->state==SND_STATE_PLAYING){
sub__sndstop(handle);
sndupdate(snd);
}
sndupdate_dont_free_resources=0;
if (snd->state==SND_STATE_STOPPED){
if (snd->use_mono){
if (!seq->data_mono){//does a mono version exist?
snd_seq_buildmono(seq);//create a mono version
sndupdate(snd);//trigger destruction of current buffer, source and the update of states
goto switch_to_mono;
}//create mono
}//needs mono
}//stopped
if (sndplay_loop){
alSourcei(snd->al_seq_source, AL_LOOPING, AL_TRUE);
}else{
alSourcei(snd->al_seq_source, AL_LOOPING, AL_FALSE);
}
alSourcePlay(snd->al_seq_source); 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
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return 0;}
if (snd->internal){error(5); return 0;}
sndupdate(snd);
if (snd->state==SND_STATE_PAUSED) return -1;
return 0;
}//sndpaused
int32 func__sndplaying(int32 handle){
if (new_error) return 0;
sndsetup();
if (handle==0) return 0;//default response
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return 0;}
if (snd->internal){error(5); return 0;}
sndupdate(snd);
if (snd->state==SND_STATE_PLAYING) return -1;
return 0;
}//func__sndplaying
double func__sndlen(int32 handle){
if (new_error) return 0;
sndsetup();
if (handle==0) return 0;//default response
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return 0;}
if (!(snd->capability&SND_CAPABILITY_LEN)){error(5); return 0;}
if (snd->internal){error(5); return 0;}
if (snd->type==2){
static snd_sequence_struct *seq; seq=snd->seq;
static int32 samples; samples=seq->data_size/seq->channels/(seq->bits_per_sample/8);
static double seconds; seconds=((double)samples)/((double)seq->sample_rate);
return seconds;
}//2
error(5); return 0;
}
void sub__sndbal(int32 handle,double x,double y,double z,int32 passed){
//note: any optional parameter not passed is assumed to be 0 (which is what NULL equates to)
if (new_error) return;
sndsetup();
if (handle==0) return;//default response
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return;}
if (!(snd->capability&SND_CAPABILITY_VOL)){error(5); return;}
if (snd->internal){error(5); return;}
if (snd->type==2){
static snd_sequence_struct *seq; seq=snd->seq;
if (passed&(2+4)){
if ((y==0)&&(z==0)) z=0.001;//trigger 3D sound placement
}
snd->bal_x=x; snd->bal_y=y; snd->bal_z=z; snd->bal_update=1;
sndupdate(snd);
}//2
//...
}//sndbal
double func__sndgetpos(int32 handle){
if (new_error) return 0;
sndsetup();
if (handle==0) return 0;//default response
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return 0;}
if (snd->internal){error(5); return 0;}
if (snd->type==2){
if (snd->al_seq_source){
static float seconds;
alGetSourcef(snd->al_seq_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;
if (seconds<0){error(5); return;}
sndsetup();
if (handle==0) return;//default response
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return;}
if (!(snd->capability&SND_CAPABILITY_SETPOS)){error(5); return;}
if (snd->internal){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;
if (limit<0){error(5); return;}
sndsetup();
if (handle==0) return;//default response
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return;}
if (snd->internal){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); return;
}
//note: this is an internal command
void sndclose_now(int32 handle){
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (snd->streamed) snd_stream_handle=0;
if (snd->type==2){
//remove OpenAL content
if (snd->al_seq_source){
alDeleteSources(1,&snd->al_seq_source); snd->al_seq_source=0;
alDeleteBuffers(1,&snd->al_seq_buffer); snd->al_seq_buffer=0;
}
static snd_sequence_struct *seq; seq=snd->seq;
//remove sound handle
list_remove(snd_handles,handle);
//remove seq
if (seq->references>1){
seq->references--;
}else{
free(seq->data);
if (seq->data_stereo){
if (seq->data_stereo!=seq->data) free(seq->data_stereo);
}
if (seq->data_mono){
if (seq->data_mono!=seq->data) free(seq->data_mono);
}
static int32 seq_handle; seq_handle=list_get_index(snd_sequences,seq);
list_remove(snd_sequences,seq_handle);
}
}//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
static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle);
if (!snd){error(5); return;}
if (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();
}//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++;
}
return ((double)((dest_buffers+source_buffers)*(snd_buffer_size/2/2)))/(double)snd_frequency;
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