Sigrid Solveig Haflínudóttirupdateea5b4b33 years agoHistory
443 lines · 12.2 KB · C++
RawDownload
/*----------------------------------------------------------------------------  ChucK Concurrent, On-the-fly Audio Programming Language    Compiler and Virtual Machine   Copyright (c) 2004 Ge Wang and Perry R. Cook.  All rights reserved.    http://chuck.stanford.edu/    http://chuck.cs.princeton.edu/   This program 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.   This program 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  U.S.A.-----------------------------------------------------------------------------*/ #include "chuck_audio.h"#include "chuck_errmsg.h"#include "util_thread.h"#include <unistd.h>#include <al.h>#include <alc.h>#define AL_ALEXT_PROTOTYPES#include <alext.h> // sample#if defined(__CHUCK_USE_64_BIT_SAMPLE__)#define AUDIO_FORMAT AL_FORMAT_STEREO_DOUBLE_EXT#else#define AUDIO_FORMAT AL_FORMAT_STEREO_FLOAT32#endif // real-time watch dogt_CKBOOL g_do_watchdog = FALSE;// countermeasure#ifdef __MACOSX_CORE__t_CKUINT g_watchdog_countermeasure_priority = 10;#elif defined(__PLATFORM_WIN32__) && !defined(__WINDOWS_PTHREAD__)t_CKUINT g_watchdog_countermeasure_priority = THREAD_PRIORITY_LOWEST;#elset_CKUINT g_watchdog_countermeasure_priority = 0;#endif// watchdog timeoutt_CKFLOAT g_watchdog_timeout = 0.5; // static initializationt_CKBOOL ChuckAudio::m_init = FALSE;t_CKBOOL ChuckAudio::m_start = FALSE;t_CKBOOL ChuckAudio::m_go = FALSE;t_CKBOOL ChuckAudio::m_silent = FALSE;t_CKBOOL ChuckAudio::m_expand_in_mono2stereo= FALSE;t_CKUINT ChuckAudio::m_num_channels_out = NUM_CHANNELS_DEFAULT;t_CKUINT ChuckAudio::m_num_channels_in = NUM_CHANNELS_DEFAULT;t_CKUINT ChuckAudio::m_num_channels_max = NUM_CHANNELS_DEFAULT;t_CKUINT ChuckAudio::m_sample_rate = SAMPLE_RATE_DEFAULT;t_CKUINT ChuckAudio::m_buffer_size = BUFFER_SIZE_DEFAULT;t_CKUINT ChuckAudio::m_num_buffers = NUM_BUFFERS_DEFAULT;SAMPLE * ChuckAudio::m_buffer_out = NULL;SAMPLE * ChuckAudio::m_buffer_in = NULL;SAMPLE * ChuckAudio::m_extern_in = NULL;SAMPLE * ChuckAudio::m_extern_out = NULL;f_audio_cb ChuckAudio::m_audio_cb = NULL;void * ChuckAudio::m_cb_user_data = NULL;t_CKINT ChuckAudio::m_adc_n = 0;t_CKINT ChuckAudio::m_dac_n = 0;std::string ChuckAudio::m_adc_name = "";std::string ChuckAudio::m_dac_name = "";std::string ChuckAudio::m_driver_name = ""; static ALCdevice *aldev = NULL;static ALCcontext *alc = NULL;static ALuint albuf;static ALuint alsrc;static pthread_t althread; void ChuckAudio::probe( const char * driver ){} t_CKINT ChuckAudio::device_named( const char * driver,                                  const std::string & name, t_CKBOOL needs_dac,                                  t_CKBOOL needs_adc ){	if(needs_adc || !needs_dac)		return -1;	const char *dev = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);	if(dev == NULL)		return -1;	const char *s = dev;	for(int i = 1; *s; i++){		if(strcmp(s, name.c_str()) == 0)			return i;		s += strlen(s) + 1;	} 	for(int i = 1; *s; i++){		if(strncmp(s, name.c_str(), name.length()) == 0)			return i;		s += strlen(s) + 1;	}     return -1;} static t_CKFLOAT get_current_time( t_CKBOOL fresh = TRUE ){#ifdef __PLATFORM_WIN32__    struct _timeb t;    _ftime(&t);    return t.time + t.millitm/1000.0;#else    static struct timeval t;    if( fresh ) gettimeofday(&t,NULL);    return t.tv_sec + (t_CKFLOAT)t.tv_usec/1000000;#endif} // watch dog globalsstatic CHUCK_THREAD g_tid_synthesis = 0;static XThread * g_watchdog_thread = NULL;static t_CKBOOL g_watchdog_state = FALSE;static t_CKFLOAT g_watchdog_time = 0; #if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)static void * watch_dog( void * )#elsestatic unsigned int __stdcall watch_dog( void * )#endif{    // time    t_CKFLOAT time = 0;     // boost priority?    t_CKUINT priority = XThreadUtil::our_priority;     // log    EM_log( CK_LOG_SEVERE, "starting real-time watch dog process..." );    // push log    EM_pushlog();    EM_log( CK_LOG_INFO, "watchdog timeout: %f::second", g_watchdog_timeout );    EM_log( CK_LOG_INFO, "watchdog thread priority: %d", priority );    EM_log( CK_LOG_INFO, "watchdog countermeasure priority: %d", g_watchdog_countermeasure_priority );    // pop log    EM_poplog();     // boost watchdog by same priority    if( XThreadUtil::our_priority != 0x7fffffff )        XThreadUtil::set_priority( priority );     // while going    while( g_do_watchdog )    {        // get        time = get_current_time( TRUE );        // CK_FPRINTF_STDERR( "last: %f now: %f\n", g_watchdog_time, time );         // resting        if( g_watchdog_state == FALSE )        {            // check time vs timeout            if( time - g_watchdog_time > g_watchdog_timeout )            {                // log                EM_log( CK_LOG_SEVERE, "real-time watchdog counter-measure activating..." );                // lowering priority                if( g_tid_synthesis && XThreadUtil::our_priority != 0x7fffffff )                    XThreadUtil::set_priority( g_tid_synthesis, g_watchdog_countermeasure_priority );                // set state                g_watchdog_state = TRUE;            }        }        else        {            // check time vs timeout            if( time - g_watchdog_time < g_watchdog_timeout )            {                // log                EM_log( CK_LOG_SEVERE, "real-time watchdog resting..." );                // raise priority                if( g_tid_synthesis && XThreadUtil::our_priority != 0x7fffffff )                    XThreadUtil::set_priority( g_tid_synthesis, XThreadUtil::our_priority );                // set state                g_watchdog_state = FALSE;            }        }                // advance time        usleep( 40000 );    }        // log    EM_log( CK_LOG_SEVERE, "stopping real-time watch dog process..." );     return 0;} t_CKBOOL ChuckAudio::watchdog_start(){    // already    if( g_watchdog_thread )        return FALSE;     // flag    g_do_watchdog = TRUE;    // allocate it    g_watchdog_thread = new XThread;    // start it    g_watchdog_thread->start( watch_dog, NULL );        return TRUE;} t_CKBOOL ChuckAudio::watchdog_stop(){    // already    if( !g_watchdog_thread )        return FALSE;     // stop things    g_do_watchdog = FALSE;    // wait a bit | TODO: is this needed?    usleep( 100000 );    // delete watchdog thread | TODO: is this safe?    SAFE_DELETE( g_watchdog_thread );        return TRUE;} ALsizei ChuckAudio::alcb(ALvoid *userptr, ALvoid *output_buffer, ALsizei len){    // priority boost    if( !m_go && XThreadUtil::our_priority != 0x7fffffff )    {#if !defined(__PLATFORM_WIN32__) || defined(__WINDOWS_PTHREAD__)        g_tid_synthesis = pthread_self();#else        // must duplicate for handle to be usable by other threads        g_tid_synthesis = NULL;        DuplicateHandle(            GetCurrentProcess(),            GetCurrentThread(),            GetCurrentProcess(),            &g_tid_synthesis,            0,            FALSE,            DUPLICATE_SAME_ACCESS        );         // TODO: close the duplicate handle?#endif        XThreadUtil::set_priority( XThreadUtil::our_priority );        memset( output_buffer, 0, len );        m_go = TRUE;         // start watchdog        if( g_do_watchdog )        {            // timestamp            g_watchdog_time = get_current_time( TRUE );            // start watchdog            watchdog_start();        }         // let it go the first time        return len;    }     // timestamp    if( g_do_watchdog ) g_watchdog_time = get_current_time( TRUE );    // call the audio cb    m_audio_cb( m_buffer_in, m_buffer_out, len/(sizeof(SAMPLE) * m_num_channels_out),        m_num_channels_in, m_num_channels_out, m_cb_user_data );     // copy local buffer to be rendered    // REFACTOR-2017: TODO: m_end was the signal to end, what should it be now?    if( m_start ) // REFACTOR-2017: now using m_start to gate this    {        memcpy( output_buffer, m_buffer_out, len );    }    // set all elements of local buffer to silence    else    {        memset( output_buffer, 0, len );    }     // copy to extern    if( m_extern_out ) memcpy( m_extern_out, output_buffer, len );     return len;} const char * ChuckAudio::defaultDriverName(){	return "";} t_CKBOOL ChuckAudio::initialize( t_CKUINT dac_device,                                 t_CKUINT adc_device,                                 t_CKUINT num_dac_channels,                                 t_CKUINT num_adc_channels,                                 t_CKUINT sample_rate,                                 t_CKUINT buffer_size,                                 t_CKUINT num_buffers,                                 f_audio_cb callback,                                 void * data,                                 t_CKBOOL force_srate, // force_srate | 1.3.1.2 (added)                                 const char * driver // NULL means default for build | 1.5.0.0 (added)                                ){    if( m_init )        return FALSE;     m_num_channels_out = num_dac_channels;    m_num_channels_in = num_adc_channels;    m_sample_rate = sample_rate;    m_buffer_size = buffer_size;    m_num_buffers = num_buffers;    m_start = 0;    m_go = 0;    m_silent = 0;        // remember callback    m_audio_cb = callback;    // remember user data to pass to callback    m_cb_user_data = data;     // variable to pass by reference into OpenAL    unsigned int bufsize = (unsigned int)m_buffer_size;     // log    EM_log( CK_LOG_FINE, "initializing OpenAL..." );    // push indent    EM_pushlog(); 	const char *dev = NULL;	if(m_dac_n > 0){		const char *s = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);		for(int i = 1; *s; i++){			if(i == m_dac_n){				dev = s;				break;			}			s += strlen(s) + 1;		}	} 	const ALCint attrlist[] = {ALC_FREQUENCY, (ALCint)sample_rate, 0, 0}; 	althread = pthread_self();	if((aldev = alcOpenDevice(dev)) == NULL)		goto error;	if((alc = alcCreateContext(aldev, attrlist)) == NULL)		goto error;	alcMakeContextCurrent(alc); 	alGenSources(1, &alsrc);	alGenBuffers(1, &albuf);	alBufferCallbackSOFT(albuf, AUDIO_FORMAT, sample_rate, alcb, NULL);	alSourcei(alsrc, AL_SOURCE_SPATIALIZE_SOFT, AL_FALSE);	alSourcei(alsrc, AL_BUFFER, albuf);     // pop indent    EM_poplog();     // allocate buffers    m_buffer_in = new SAMPLE[m_buffer_size * m_num_channels_max];    m_buffer_out = new SAMPLE[m_buffer_size * m_num_channels_max];    memset( m_buffer_in, 0, m_buffer_size * sizeof(SAMPLE) * m_num_channels_max );    memset( m_buffer_out, 0, m_buffer_size * sizeof(SAMPLE) * m_num_channels_max );     // set flag and return    return m_init = TRUE; // handle common error case (added 1.4.1.0)error:	if(alc != NULL){		alcDestroyContext(alc);		alc = NULL;	}	if(aldev != NULL){		alcCloseDevice(aldev);		aldev = NULL;	}    // pop log indent (added 1.4.1.0)    EM_poplog();    // set flag and return    return m_init = FALSE;} t_CKBOOL ChuckAudio::start( ){	alSourcePlay(alsrc);	if( !m_start )		alcDeviceResumeSOFT(aldev);	m_start = TRUE;     return m_start;} t_CKBOOL ChuckAudio::stop( ){	alSourceStop(alsrc);	if( m_start )		alcDevicePauseSOFT(aldev);	m_start = FALSE;        // REFACTOR-2017: set end flag    m_silent = TRUE;     return !m_start;} void ChuckAudio::shutdown(){    // check flag    if( !m_init ) return;     // stop watchdog | REFACTOR-2017: moved this earlier to give watchdog    // a head start in stopping, to avoid unneeded countermeasure deployment    watchdog_stop(); 	if(althread == pthread_self() && alc != NULL){		alSourceStop(alsrc);		alDeleteSources(1, &alsrc);		alDeleteBuffers(1, &albuf);		alcDestroyContext(alc);		alc = NULL;		alcCloseDevice(aldev);		aldev = NULL;	}     // unflag    m_init = FALSE;    m_start = FALSE;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t