/* * 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 "aedit.h" #include "amodule.h" #include "arender.h" #include "assets.h" #include "atrack.h" #include "audiodevice.h" #include "bcsignals.h" #include "condition.h" #include "edit.h" #include "edits.h" #include "edl.h" #include "edlsession.h" #include "file.h" #include "levelwindow.h" #include "playabletracks.h" #include "plugin.h" #include "preferences.h" #include "renderengine.h" #include "samples.h" #include "thread.h" #include "tracks.h" #include "transportque.h" #include "virtualaconsole.h" #include "virtualanode.h" #include "virtualnode.h" VirtualAConsole::VirtualAConsole(RenderEngine *renderengine, ARender *arender) : VirtualConsole(renderengine, arender, TRACK_AUDIO) { this->arender = arender; output_temp = 0; output_allocation = 0; } VirtualAConsole::~VirtualAConsole() { if(output_temp) delete output_temp; } void VirtualAConsole::get_playable_tracks() { if(!playable_tracks) playable_tracks = new PlayableTracks(renderengine->get_edl(), commonrender->current_position, renderengine->command->get_direction(), TRACK_AUDIO, 1); } VirtualNode* VirtualAConsole::new_entry_node(Track *track, Module *module, int track_number) { return new VirtualANode(renderengine, this, module, 0, track, 0); return 0; } int VirtualAConsole::process_buffer(int64_t len, int64_t start_position) { int result = 0; const int debug = 0; if(debug) printf("VirtualAConsole::process_buffer %d this=%p len=%jd\n", __LINE__, this, len); // clear output buffers for(int i = 0; i < MAX_CHANNELS; i++) { // if(debug) printf("VirtualAConsole::process_buffer 2 %d %p %jd\n", // i, // arender->audio_out[i], // len); if(arender->audio_out[i]) { bzero(arender->audio_out[i]->get_data(), len * sizeof(double)); } } // Create temporary output if(output_temp && output_allocation < len) { delete output_temp; output_temp = 0; } if(!output_temp) { output_temp = new Samples(len); output_allocation = len; } if(debug) printf("VirtualAConsole::process_buffer %d\n", __LINE__); // Reset plugin rendering status reset_attachments(); //printf("VirtualAConsole::process_buffer 1 %p\n", output_temp); if(debug) printf("VirtualAConsole::process_buffer %d\n", __LINE__); // Render exit nodes for(int i = 0; i < exit_nodes.total; i++) { VirtualANode *node = (VirtualANode*)exit_nodes.values[i]; Track *track = node->track; result |= node->render(output_temp, len, start_position + track->nudge, renderengine->get_edl()->session->sample_rate); } if(debug) printf("VirtualAConsole::process_buffer %d\n", __LINE__); // get peaks and limit volume in the fragment for(int i = 0; i < MAX_CHANNELS; i++) { if(arender->audio_out[i]) { double *current_buffer = arender->audio_out[i]->get_data(); for(int j = 0; j < len; ) { int meter_render_end; // Get length to test for meter and limit if(renderengine->command->realtime) meter_render_end = j + arender->meter_render_fragment; else meter_render_end = len; if(meter_render_end > len) meter_render_end = len; double peak = 0; while( j < meter_render_end ) { // Level history comes before clipping to get over status double *sample = ¤t_buffer[j++]; if( fabs(*sample) > peak ) peak = fabs(*sample); // Make the output device clip it // if(*sample > 1) *sample = 1; // else // if(*sample < -1) *sample = -1; } if( renderengine->command->realtime ) { int direction = renderengine->command->get_direction(); int64_t pos = direction == PLAY_REVERSE ? start_position - j : start_position + j ; arender->meter_history->set_peak(i, peak, pos); } } } } if(debug) printf("VirtualAConsole::process_buffer %d\n", __LINE__); // Pack channels, fix speed and send to device. if(!renderengine->is_nested && renderengine->command->realtime && !interrupt) { // speed parameters float speed = renderengine->command->get_speed(); // length compensated for speed int real_output_len = 0; double *audio_out_packed[MAX_CHANNELS]; int audio_channels = renderengine->get_edl()->session->audio_channels; for( int i=0; iaudio_out[i]->get_data(); // Time stretch the fragment to the real_output size for( int i=0; i 1 ) { // buffer gets shorter int in = 0, out = 0; while( in < len ) { int next = (out+1) * speed; if( next > len) next = len; double sample = current_buffer[in]; for( int i=in; ++i 1 ) sample /= l; current_buffer[out++] = sample; in = next; } real_output_len = out; } else if( speed < 1 ) { // buffer gets longer real_output_len = len / speed; int in = len, out = real_output_len; while( in > 0 && out > 0 ) { double sample = current_buffer[--in]; int next = in / speed; while( out > next ) current_buffer[--out] = sample; } } else real_output_len = len; } // Wait until video is ready if( arender->first_buffer ) { renderengine->first_frame_lock->lock("VirtualAConsole::process_buffer"); arender->first_buffer = 0; } if( !renderengine->audio->get_interrupted() ) { renderengine->audio->write_buffer(audio_out_packed, audio_channels, real_output_len); } if( renderengine->audio->get_interrupted() ) interrupt = 1; } return result; } int VirtualAConsole::init_rendering(int duplicate) { return 0; } int VirtualAConsole::send_last_output_buffer() { renderengine->audio->set_last_buffer(); return 0; }