/* * 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 // length compensated for speed int real_output_len = 0; // output sample double sample; int k; double *audio_out_packed[MAX_CHANNELS]; int audio_channels = renderengine->get_edl()->session->audio_channels; for(int i = 0, j = 0; i < audio_channels; i++) { audio_out_packed[j++] = arender->audio_out[i]->get_data(); } for(int i = 0; i < audio_channels; i++) { int in, out; double *current_buffer = audio_out_packed[i]; // Time stretch the fragment to the real_output size if(renderengine->command->get_speed() > 1) { // Number of samples in real output buffer for each to sample rendered. int interpolate_len = (int)renderengine->command->get_speed(); for(in = 0, out = 0; in < len; ) { sample = 0; for(k = 0; k < interpolate_len; k++) { sample += current_buffer[in++]; } sample /= renderengine->command->get_speed(); current_buffer[out++] = sample; } real_output_len = out; } else if(renderengine->command->get_speed() < 1) { // number of samples to skip int interpolate_len = (int)(1.0 / renderengine->command->get_speed()); real_output_len = len * interpolate_len; for(in = len - 1, out = real_output_len - 1; in >= 0; ) { for(k = 0; k < interpolate_len; k++) { current_buffer[out--] = current_buffer[in]; } in--; } } 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; } if(debug) printf("VirtualAConsole::process_buffer %d\n", __LINE__); return result; } int VirtualAConsole::init_rendering(int duplicate) { return 0; } int VirtualAConsole::send_last_output_buffer() { renderengine->audio->set_last_buffer(); return 0; }