/* * lingot, a musical instrument tuner. * * Copyright (C) 2004-2018 Iban Cereijo. * Copyright (C) 2004-2008 Jairo Chapela. * * This file is part of lingot. * * lingot 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. * * lingot 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 lingot; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "lingot-defs.h" #include "lingot-audio.h" #include "lingot-core.h" #include "lingot-audio-oss.h" #include "lingot-audio-alsa.h" #include "lingot-audio-jack.h" #include "lingot-audio-pulseaudio.h" #include "lingot-i18n.h" #include "lingot-msg.h" void lingot_audio_new(LingotAudioHandler* result, audio_system_t audio_system, const char* device, int sample_rate, LingotAudioProcessCallback process_callback, void *process_callback_arg) { # if !defined(OSS) && !defined(ALSA) && !defined(JACK) && !defined(PULSEAUDIO) # error "No audio system has been defined" # endif switch (audio_system) { # ifdef OSS case AUDIO_SYSTEM_OSS: lingot_audio_oss_new(result, device, sample_rate); # else lingot_msg_add_error( _("The application has not been built with OSS support")); result->audio_system = -1; # endif break; case AUDIO_SYSTEM_ALSA: # ifdef ALSA lingot_audio_alsa_new(result, device, sample_rate); # else lingot_msg_add_error( _("The application has not been built with ALSA support")); result->audio_system = -1; # endif break; case AUDIO_SYSTEM_JACK: # ifdef JACK lingot_audio_jack_new(result, device); # else lingot_msg_add_error( _("The application has not been built with JACK support")); result->audio_system = -1; # endif break; # ifdef PULSEAUDIO case AUDIO_SYSTEM_PULSEAUDIO: lingot_audio_pulseaudio_new(result, device, sample_rate); # else lingot_msg_add_error( _("The application has not been built with PULSEAUDIO support")); result->audio_system = -1; # endif break; default: assert (0); } if (result->audio_system != -1 ) { // audio source read in floating point format. result->flt_read_buffer = malloc( result->read_buffer_size_samples * sizeof(FLT)); memset(result->flt_read_buffer, 0, result->read_buffer_size_samples * sizeof(FLT)); result->process_callback = process_callback; result->process_callback_arg = process_callback_arg; result->interrupted = 0; result->running = 0; } } void lingot_audio_destroy(LingotAudioHandler* audio) { switch (audio->audio_system) { # ifdef OSS case AUDIO_SYSTEM_OSS: lingot_audio_oss_destroy(audio); break; # endif # ifdef ALSA case AUDIO_SYSTEM_ALSA: lingot_audio_alsa_destroy(audio); break; # endif # ifdef JACK case AUDIO_SYSTEM_JACK: lingot_audio_jack_destroy(audio); break; # endif # ifdef PULSEAUDIO case AUDIO_SYSTEM_PULSEAUDIO: lingot_audio_pulseaudio_destroy(audio); break; # endif default: assert (0); } if (audio->flt_read_buffer != 0x0) { free(audio->flt_read_buffer); audio->flt_read_buffer = 0x0; } audio->audio_system = -1; } int lingot_audio_read(LingotAudioHandler* audio) { int samples_read = -1; switch (audio->audio_system) { # ifdef OSS case AUDIO_SYSTEM_OSS: samples_read = lingot_audio_oss_read(audio); break; # endif # ifdef ALSA case AUDIO_SYSTEM_ALSA: samples_read = lingot_audio_alsa_read(audio); break; # endif # ifdef PULSEAUDIO case AUDIO_SYSTEM_PULSEAUDIO: samples_read = lingot_audio_pulseaudio_read(audio); break; # endif default: assert (0); } //# define RATE_ESTIMATOR # ifdef RATE_ESTIMATOR static double samplerate_estimator = 0.0; static unsigned long read_samples = 0; static double elapsed_time = 0.0; struct timeval tdiff, t_abs; static struct timeval t_abs_old = { .tv_sec = 0, .tv_usec = 0 }; static FILE* fid = 0x0; if (fid == 0x0) { fid = fopen("/tmp/dump.txt", "w"); } gettimeofday(&t_abs, NULL ); if ((t_abs_old.tv_sec != 0) || (t_abs_old.tv_usec != 0)) { int i; for (i = 0; i < samples_read; i++) { fprintf(fid, "%f ", audio->flt_read_buffer[i]); // printf("%f ", audio->flt_read_buffer[i]); } // printf("\n"); timersub(&t_abs, &t_abs_old, &tdiff); read_samples = samples_read; elapsed_time = tdiff.tv_sec + 1e-6 * tdiff.tv_usec; static const double c = 0.9; samplerate_estimator = c * samplerate_estimator + (1 - c) * (read_samples / elapsed_time); // printf("estimated sample rate %f (read %i samples in %f seconds)\n", // samplerate_estimator, read_samples, elapsed_time); } t_abs_old = t_abs; # endif return samples_read; } int lingot_audio_get_audio_system_properties( LingotAudioSystemProperties* properties, audio_system_t audio_system) { switch (audio_system) { # ifdef OSS case AUDIO_SYSTEM_OSS: return lingot_audio_oss_get_audio_system_properties(properties); # endif # ifdef ALSA case AUDIO_SYSTEM_ALSA: return lingot_audio_alsa_get_audio_system_properties(properties); # endif # ifdef JACK case AUDIO_SYSTEM_JACK: return lingot_audio_jack_get_audio_system_properties(properties); # endif # ifdef PULSEAUDIO case AUDIO_SYSTEM_PULSEAUDIO: return lingot_audio_pulseaudio_get_audio_system_properties(properties); # endif default: assert (0); } return -1; } void lingot_audio_audio_system_properties_destroy( LingotAudioSystemProperties* properties) { int i; if (properties->devices != NULL) { for (i = 0; i < properties->n_devices; i++) { if (properties->devices[i] != NULL ) { free(properties->devices[i]); } } free(properties->devices); } } void lingot_audio_run_reading_thread(LingotAudioHandler* audio) { int samples_read = 0; while (audio->running) { // process new data block. samples_read = lingot_audio_read(audio); if (samples_read < 0) { audio->running = 0; audio->interrupted = 1; } else { audio->process_callback(audio->flt_read_buffer, samples_read, audio->process_callback_arg); } } pthread_mutex_lock(&audio->thread_input_read_mutex); pthread_cond_broadcast(&audio->thread_input_read_cond); pthread_mutex_unlock(&audio->thread_input_read_mutex); } int lingot_audio_start(LingotAudioHandler* audio) { int result = 0; switch (audio->audio_system) { case AUDIO_SYSTEM_JACK: # ifdef JACK result = lingot_audio_jack_start(audio); # else assert (0); # endif break; default: pthread_attr_init(&audio->thread_input_read_attr); // detached thread. // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_mutex_init(&audio->thread_input_read_mutex, NULL ); pthread_cond_init(&audio->thread_input_read_cond, NULL ); pthread_attr_init(&audio->thread_input_read_attr); pthread_create(&audio->thread_input_read, &audio->thread_input_read_attr, (void* (*)(void*)) lingot_audio_run_reading_thread, audio); break; } if (result == 0) { audio->running = 1; } return result; } // function invoked when the audio thread must be cancelled void lingot_audio_cancel(LingotAudioHandler* audio) { // TODO: avoid fprintf(stderr, "warning: canceling audio thread\n"); switch (audio->audio_system) { # ifdef PULSEAUDIO case AUDIO_SYSTEM_PULSEAUDIO: lingot_audio_pulseaudio_cancel(audio); break; # endif default: break; } } void lingot_audio_stop(LingotAudioHandler* audio) { void* thread_result; int result; struct timeval tout, tout_abs; struct timespec tout_tspec; gettimeofday(&tout_abs, NULL ); tout.tv_sec = 0; tout.tv_usec = 500000; if (audio->running == 1) { audio->running = 0; switch (audio->audio_system) { case AUDIO_SYSTEM_JACK: # ifdef JACK lingot_audio_jack_stop(audio); # else assert (0); # endif break; // case AUDIO_SYSTEM_PULSEAUDIO: // pthread_join(audio->thread_input_read, &thread_result); // pthread_attr_destroy(&audio->thread_input_read_attr); // pthread_mutex_destroy(&audio->thread_input_read_mutex); // pthread_cond_destroy(&audio->thread_input_read_cond); // break; default: timeradd(&tout, &tout_abs, &tout_abs); tout_tspec.tv_sec = tout_abs.tv_sec; tout_tspec.tv_nsec = 1000 * tout_abs.tv_usec; // watchdog timer pthread_mutex_lock(&audio->thread_input_read_mutex); result = pthread_cond_timedwait(&audio->thread_input_read_cond, &audio->thread_input_read_mutex, &tout_tspec); pthread_mutex_unlock(&audio->thread_input_read_mutex); if (result == ETIMEDOUT) { pthread_cancel(audio->thread_input_read); lingot_audio_cancel(audio); } else { pthread_join(audio->thread_input_read, &thread_result); } pthread_attr_destroy(&audio->thread_input_read_attr); pthread_mutex_destroy(&audio->thread_input_read_mutex); pthread_cond_destroy(&audio->thread_input_read_cond); break; } } }