/* * CINELERRA * Copyright (C) 2008 Adam Williams * * 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 USA * */ #include "asset.h" #include "audiodevice.h" #include "bcdisplayinfo.h" #include "bcsignals.h" #include "clip.h" #include "bchash.h" #include "edl.h" #include "edlsession.h" #include "filexml.h" #include "guicast.h" #include "language.h" #include "mwindow.h" #include "pluginaclient.h" #include "pluginserver.h" #include "recordconfig.h" #include "samples.h" #include "transportque.inc" #include "vframe.h" #include #include class LiveAudio; class LiveAudioWindow; class LiveAudioConfig { public: LiveAudioConfig(); }; class LiveAudioWindow : public PluginClientWindow { public: LiveAudioWindow(LiveAudio *plugin); ~LiveAudioWindow(); void create_objects(); LiveAudio *plugin; }; class LiveAudio : public PluginAClient { public: LiveAudio(PluginServer *server); ~LiveAudio(); PLUGIN_CLASS_MEMBERS(LiveAudioConfig); int process_buffer(int64_t size, Samples **buffer, int64_t start_position, int sample_rate); int is_realtime(); int is_multichannel(); int is_synthesis(); void save_data(KeyFrame *keyframe); void read_data(KeyFrame *keyframe); void update_gui(); void render_stop(); AudioDevice *adevice; Samples **history; int history_ptr; int history_channels; int64_t history_position; int history_size; // Fragment size from the EDL session int fragment_size; }; LiveAudioConfig::LiveAudioConfig() { } LiveAudioWindow::LiveAudioWindow(LiveAudio *plugin) : PluginClientWindow(plugin, xS(300), yS(160), xS(300), yS(160), 0) { this->plugin = plugin; } LiveAudioWindow::~LiveAudioWindow() { } void LiveAudioWindow::create_objects() { int x = xS(10), y = yS(10); BC_Title *title; add_subwindow(title = new BC_Title(x, y, _("Live audio"))); show_window(); flush(); } REGISTER_PLUGIN(LiveAudio) LiveAudio::LiveAudio(PluginServer *server) : PluginAClient(server) { adevice = 0; history = 0; history_channels = 0; history_ptr = 0; history_position = 0; history_size = 0; fragment_size = 0; } LiveAudio::~LiveAudio() { if(adevice) { adevice->interrupt_crash(); adevice->close_all(); } delete adevice; if(history) { for(int i = 0; i < history_channels; i++) delete history[i]; delete [] history; } } int LiveAudio::process_buffer(int64_t size, Samples **buffer, int64_t start_position, int sample_rate) { load_configuration(); // printf("LiveAudio::process_buffer 10 size=%lld buffer_size=%d channels=%d size=%d\n", // size, get_buffer_size(), get_total_buffers(), size); int first_buffer = 0; if(!adevice) { EDLSession *session = get_edl()->session; if(session) { fragment_size = session->record_fragment_size; history_channels = session->aconfig_in->channels; adevice = new AudioDevice(server ? server->mwindow : 0); // Take fragment size & channels from the recording config adevice->open_input(session->aconfig_in, session->vconfig_in, get_project_samplerate(), fragment_size, session->aconfig_in->channels, session->real_time_record); adevice->start_recording(); first_buffer = 1; history_position = start_position; } } if(!history || history[0]->get_allocated() < size) { // compute new history which is a multiple of our fragment size int new_history_size = fragment_size; while(new_history_size < size) new_history_size += fragment_size; if(!history) { history = new Samples*[history_channels]; for(int i = 0; i < history_channels; i++) history[i] = 0; } for(int i = 0; i < history_channels; i++) { delete history[i]; history[i] = new Samples(new_history_size); bzero(history[i]->get_data(), sizeof(double) * new_history_size); } } // if(get_direction() == PLAY_FORWARD) { // Reset history buffer to current position if before maximum history if(start_position < history_position - history[0]->get_allocated()) history_position = start_position; // Extend history buffer int64_t end_position = start_position + size; // printf("LiveAudio::process_buffer %lld %lld %lld\n", // end_position, // history_position, // end_position - history_position); if(end_position > history_position) { // Reset history buffer to current position if after maximum history if(start_position >= history_position + history[0]->get_allocated()) history_position = start_position; // A delay seems required because ALSA playback may get ahead of // ALSA recording and never recover. if(first_buffer) end_position += sample_rate; int done = 0; while(!done && history_position < end_position) { if(history_ptr + fragment_size >= history[0]->get_allocated()) { done = 1; } // Read rest of buffer from sound driver if(adevice) { int over[history_channels]; double max[history_channels]; int result = adevice->read_buffer(history, history_channels, fragment_size, over, max, history_ptr); if( result && adevice->config_updated() ) adevice->config_update(); } history_ptr += fragment_size; // wrap around buffer if(history_ptr >= history[0]->get_allocated()) history_ptr = 0; history_position += fragment_size; } } // Copy data from history buffer int buffer_position = 0; int history_buffer_ptr = history_ptr - history_position + start_position; while(history_buffer_ptr < 0) history_buffer_ptr += history[0]->get_allocated(); while(buffer_position < size) { int fragment = size; if(history_buffer_ptr + fragment > history[0]->get_allocated()) fragment = history[0]->get_allocated() - history_buffer_ptr; if(buffer_position + fragment > size) fragment = size - buffer_position; for(int i = 0; i < get_total_buffers(); i++) { int input_channel = i; // Clamp channel to total history channels if(input_channel >= history_channels) input_channel = history_channels - 1; memcpy(buffer[i]->get_data() + buffer_position, history[input_channel]->get_data() + history_buffer_ptr, sizeof(double) * fragment); } history_buffer_ptr += fragment; if(history_buffer_ptr >= history[0]->get_allocated()) history_buffer_ptr = 0; buffer_position += fragment; } } return 0; } void LiveAudio::render_stop() { if(adevice) { adevice->interrupt_crash(); adevice->close_all(); } delete adevice; adevice = 0; history_ptr = 0; history_position = 0; history_size = 0; } const char* LiveAudio::plugin_title() { return N_("Live Audio"); } int LiveAudio::is_realtime() { return 1; } int LiveAudio::is_multichannel() { return 1; } int LiveAudio::is_synthesis() { return 1; } NEW_WINDOW_MACRO(LiveAudio, LiveAudioWindow) int LiveAudio::load_configuration() { return 0; } void LiveAudio::save_data(KeyFrame *keyframe) { } void LiveAudio::read_data(KeyFrame *keyframe) { } void LiveAudio::update_gui() { }