diff --git a/veejay-current/veejay-server/libveejay/liblavplayvj.c b/veejay-current/veejay-server/libveejay/liblavplayvj.c index 40915e28..ee30a0b1 100644 --- a/veejay-current/veejay-server/libveejay/liblavplayvj.c +++ b/veejay-current/veejay-server/libveejay/liblavplayvj.c @@ -2137,6 +2137,9 @@ void veejay_sample_set_initial_positions(veejay_t *info) int id = info->seq->samples[i].sample_id; + if(id <= 0 || !sample_exists(id)) + continue; + sample_set_loops(id, -1); sample_set_resume_override(id, -1); @@ -2182,10 +2185,14 @@ void veejay_reset_sample_positions(veejay_t *info, int sample_id) if( info->seq->samples[i].type != 0 ) continue; int id = info->seq->samples[i].sample_id; + if(id <= 0 || !sample_exists(id)) + continue; veejay_prepare_sample_positions(id); } } else { + if(sample_id <= 0 || !sample_exists(sample_id)) + return; veejay_prepare_sample_positions(sample_id); } } @@ -4553,6 +4560,9 @@ static inline int vj_audio_is_external_jack_monitor_source( source != VJ_AUDIO_SYNC_SOURCE_WAV_FILE) return 0; + if(source == VJ_AUDIO_SYNC_SOURCE_WAV_FILE) + return audio_source != VJ_RECORD_AUDIO_SOURCE_SILENCE; + return (audio_source == VJ_RECORD_AUDIO_SOURCE_BEAT_JACK || audio_source == VJ_RECORD_AUDIO_SOURCE_AUTO); } @@ -4973,11 +4983,27 @@ void *veejay_audio_producer_thread(void *arg) veejay_t *info = (veejay_t *)arg; video_playback_setup *settings = info->settings; editlist *el = info->current_edit_list; + int audio_channels = 2; + int BPS = 4; + int media_audio = 0; - int has_audio = (info->current_edit_list->has_audio && - vj_perform_init_audio(info,0) && - vj_perform_init_audio(info, 1) && - info->audio == AUDIO_PLAY); + if(el) { + if(el->audio_chans > 0 && el->audio_chans <= 2) + audio_channels = el->audio_chans; + if(el->audio_bps > 0) + BPS = el->audio_bps; + } + + media_audio = (el && el->has_audio && + vj_perform_init_audio(info,0) && + vj_perform_init_audio(info,1) && + info->audio == AUDIO_PLAY); + +#ifdef HAVE_JACK + int has_audio = (info->audio == AUDIO_PLAY); +#else + int has_audio = media_audio; +#endif #ifndef HAVE_JACK has_audio = 0; #endif @@ -5004,7 +5030,6 @@ void *veejay_audio_producer_thread(void *arg) double slow_video_phase = 0.0; int last_dynamic_slow = 0; unsigned long long loop_count = 0; - const int BPS = el->audio_bps; const int MAX_CLIENT_FRAMES = (int)(((double)CLIENT_RATE / VJ_DYNAMIC_ALLOC_MIN_FPS) + 4096.0); uint8_t *audio_chunk = NULL; @@ -5154,6 +5179,18 @@ void *veejay_audio_producer_thread(void *arg) ) && external_tape_neutral_dbg && !audio_mute_dbg; + int external_monitor_audio = + vj_audio_is_external_jack_monitor_source( + settings, + audio_source_dbg, + audio_sync_mode_dbg, + audio_sync_enabled_dbg + ) && + !audio_mute_dbg; + int external_direct_passthrough = + external_monitor_audio && + external_live_source_dbg && + atomic_load_int(&settings->audio_sync.source) == VJ_AUDIO_SYNC_SOURCE_JACK; if(vj_jack_xrun_flag()) { @@ -5171,7 +5208,28 @@ void *veejay_audio_producer_thread(void *arg) } int tx_active = atomic_load_int(&settings->transition.active) && atomic_load_int(&settings->transition.global_state); - if(!tx_active) { + if(external_monitor_audio) { +#ifdef HAVE_JACK + vj_jack_set_input_passthrough(external_direct_passthrough ? 1 : 0); +#endif + if(external_direct_passthrough) { + veejay_memset(audio_chunk, 0, (size_t)needed * (size_t)BPS); + decoded = needed; + } else { + decoded = vj_audio_sync_render_monitor_s16(&settings->audio_sync, + audio_chunk, + needed, + BPS, + audio_channels, + CLIENT_RATE); + + if(decoded <= 0) { + veejay_memset(audio_chunk, 0, (size_t)needed * (size_t)BPS); + decoded = needed; + } + } + } + else if(media_audio && !tx_active) { decoded = vj_perform_queue_audio_chunk_ext(info, needed, media_frame, 0, audio_chunk); if (decoded <= 0) { @@ -5192,12 +5250,19 @@ void *veejay_audio_producer_thread(void *arg) } } } - else { + else if(media_audio) { long long b_frame = vj_calc_next_subframe(info, settings->transition.next_id); long long start = atomic_load_long_long(&settings->transition.start); long long end = atomic_load_long_long(&settings->transition.end); decoded = vj_perform_queue_audio_chunk_crossfade(info, needed, media_frame, b_frame, audio_chunk, settings->transition.next_id,start,end); } + else { +#ifdef HAVE_JACK + vj_jack_set_input_passthrough(0); +#endif + veejay_memset(audio_chunk, 0, (size_t)needed * (size_t)BPS); + decoded = needed; + } int frames_written = 0; int write_pos = 0; @@ -7496,13 +7561,34 @@ int veejay_toggle_audio(veejay_t * info, int audio) (void) audio; - if( !(el->has_audio) ) { - veejay_msg(VEEJAY_MSG_WARNING, +#ifdef HAVE_JACK + int external_audio = vj_audio_is_external_jack_monitor_source( + settings, + atomic_load_int(&settings->record_audio_source), + atomic_load_int(&settings->audio_sync.mode), + vj_audio_sync_is_enabled(&settings->audio_sync) + ); +#else + int external_audio = 0; +#endif + + int silent_output = (info->audio == AUDIO_PLAY && + el && + !el->has_audio && + el->audio_rate > 0 && + el->audio_chans > 0 && + el->audio_bps > 0); + + if((!el || !(el->has_audio)) && !external_audio && !silent_output) { + veejay_msg(VEEJAY_MSG_WARNING, "Audio playback has not been enabled"); info->audio = NO_AUDIO; return 0; } + if(external_audio || silent_output) + info->audio = AUDIO_PLAY; + old_mute = atomic_load_int(&settings->audio_mute) ? 1 : 0; new_mute = old_mute ? 0 : 1; diff --git a/veejay-current/veejay-server/libveejay/vj-event.c b/veejay-current/veejay-server/libveejay/vj-event.c index cfbf4730..cc9e2da5 100644 --- a/veejay-current/veejay-server/libveejay/vj-event.c +++ b/veejay-current/veejay-server/libveejay/vj-event.c @@ -4315,6 +4315,11 @@ void vj_event_sample_set_rand_loop(void *ptr, const char format[], va_list ap) SAMPLE_DEFAULTS(args[0]); + if(v->seq && v->seq->active && sample_exists(args[0])) { + sample_set_loop_stats(args[0], 0); + sample_set_loops(args[0], -1); + } + if( args[1] == -1 ) { if( sample_exists(args[0]) ) @@ -4351,6 +4356,13 @@ void vj_event_sample_set_loop_type(void *ptr, const char format[], va_list ap) return; } + if(v->seq && v->seq->active) { + sample_set_loop_stats(args[0], 0); + sample_set_loops(args[0], -1); + if(args[1] == 0) + args[1] = 4; + } + if(args[1] == -1) { int lp = sample_get_looptype(args[0]); if( lp == 1 && args[1] == -1 ) @@ -4458,6 +4470,12 @@ void vj_event_sample_set_speed(void *ptr, const char format[], va_list ap) SAMPLE_DEFAULTS(args[0]); + if(v->seq && v->seq->active && args[1] == 0) { + args[1] = v->settings->previous_playback_speed; + if(args[1] == 0) + args[1] = 1; + } + if( sample_set_speed(args[0], args[1]) != -1) { veejay_msg(VEEJAY_MSG_INFO, "Changed speed of sample %d to %d",args[0],args[1]); @@ -12651,6 +12669,13 @@ void vj_event_sample_sequencer_active(void *ptr, const char format[], va_list ap vj_tag_set_loops(cur_id, -1); } + if(v->settings && v->settings->current_playback_speed == 0) { + int resume_speed = v->settings->previous_playback_speed; + if(resume_speed == 0) + resume_speed = 1; + veejay_set_speed(v, resume_speed, 0); + } + veejay_msg(VEEJAY_MSG_INFO,"Sample sequencer disabled; current source keeps looping"); return; } @@ -12688,6 +12713,13 @@ void vj_event_sample_sequencer_active(void *ptr, const char format[], va_list ap v->seq->active = 1; + if(v->settings && v->settings->current_playback_speed == 0) { + int resume_speed = v->settings->previous_playback_speed; + if(resume_speed == 0) + resume_speed = 1; + veejay_set_speed(v, resume_speed, 0); + } + veejay_reset_sample_positions(v, -1); veejay_change_playback_mode(v, playback_mode, next_sample_id); @@ -12952,5 +12984,4 @@ void vj_event_alpha_composite(void *ptr, const char format[], va_list ap) v->settings->alpha_value = 255; veejay_msg(VEEJAY_MSG_INFO,"New alpha mask every frame (default alpha value is %d)", v->settings->alpha_value); } -} - +} \ No newline at end of file diff --git a/veejay-current/veejay-server/libveejay/vj-perform.c b/veejay-current/veejay-server/libveejay/vj-perform.c index ec96e917..7ffb6d13 100644 --- a/veejay-current/veejay-server/libveejay/vj-perform.c +++ b/veejay-current/veejay-server/libveejay/vj-perform.c @@ -475,6 +475,62 @@ static inline int vj_perform_clampi(int v, int lo, int hi) return (v < lo) ? lo : ((v > hi) ? hi : v); } +#ifndef VJ_SILENCE_AUDIO_RATE +#define VJ_SILENCE_AUDIO_RATE 48000 +#endif +#ifndef VJ_SILENCE_AUDIO_CHANS +#define VJ_SILENCE_AUDIO_CHANS 2 +#endif +#ifndef VJ_SILENCE_AUDIO_BITS +#define VJ_SILENCE_AUDIO_BITS 16 +#endif + +static inline editlist *vj_perform_audio_editlist(veejay_t *info) +{ + if(!info) + return NULL; + return info->current_edit_list ? info->current_edit_list : info->edit_list; +} + +static inline int vj_perform_audio_params_valid(const editlist *el) +{ + return el && + el->video_fps > 0.0 && + el->audio_rate > 0 && + el->audio_chans > 0 && + el->audio_bits > 0 && + el->audio_bps > 0; +} + +static inline int vj_perform_audio_media_valid(const editlist *el) +{ + return el && el->has_audio && vj_perform_audio_params_valid(el); +} + +#ifdef HAVE_JACK +static int vj_perform_prepare_silence_audio_params(editlist *el) +{ + if(!el || el->video_fps <= 0.0) + return 0; + + if(el->audio_rate <= 0) + el->audio_rate = VJ_SILENCE_AUDIO_RATE; + + if(el->audio_chans <= 0) + el->audio_chans = VJ_SILENCE_AUDIO_CHANS; + else if(el->audio_chans > 2) + el->audio_chans = 2; + + if(el->audio_bits <= 0 || (el->audio_bits % 8) != 0) + el->audio_bits = VJ_SILENCE_AUDIO_BITS; + + if(el->audio_bps <= 0) + el->audio_bps = el->audio_chans * (el->audio_bits / 8); + + return vj_perform_audio_params_valid(el); +} +#endif + #define CACHE_TOP 0 #define CACHE 1 @@ -1888,37 +1944,51 @@ static void vj_perform_record_buffer_free(performer_global_t *g) int vj_init_audio_fader_luts(veejay_t *info) { performer_global_t *global = (performer_global_t*) info->performer; + editlist *el = vj_perform_audio_editlist(info); - const int audio_rate = info->current_edit_list->audio_rate; - const double video_fps = info->current_edit_list->video_fps; + if(!global) + return 0; + if(!vj_perform_audio_media_valid(el)) { + if(el && !el->has_audio) + veejay_msg(VEEJAY_MSG_INFO, "[AUDIO] No embedded audio stream; audio mixer runs silent"); + else + veejay_msg(VEEJAY_MSG_WARNING, "[AUDIO] Invalid embedded audio parameters; audio mixer disabled"); + return 1; + } + + const int audio_rate = el->audio_rate; + const double video_fps = el->video_fps; + const int frame_bytes = el->audio_bps; const int max_samples_per_frame = (int)ceil((double)audio_rate / video_fps); - size_t buffer_size = max_samples_per_frame * info->current_edit_list->audio_bps; - global->fade_lut = (float*) vj_calloc( sizeof(float) * buffer_size); - if(!global->fade_lut) { - return 0; - } - global->gain_lut[0] = (float*) vj_calloc( (sizeof(float) * buffer_size)); - if(!global->gain_lut[0]) { - return 0; + if(max_samples_per_frame < 2 || frame_bytes <= 0) { + veejay_msg(VEEJAY_MSG_WARNING, "[AUDIO] Invalid mixer geometry; audio mixer disabled"); + return 1; } - global->gain_lut[1] = (float*) vj_calloc( (sizeof(float) * buffer_size)); - if(!global->gain_lut[1]) { + size_t buffer_size = (size_t)max_samples_per_frame * (size_t)frame_bytes; + + global->fade_lut = (float*) vj_calloc(sizeof(float) * buffer_size); + if(!global->fade_lut) + return 0; + + global->gain_lut[0] = (float*) vj_calloc(sizeof(float) * buffer_size); + if(!global->gain_lut[0]) + return 0; + + global->gain_lut[1] = (float*) vj_calloc(sizeof(float) * buffer_size); + if(!global->gain_lut[1]) return 0; - } for (int i = 0; i < max_samples_per_frame; i++) { float t = (float)i / (float)(max_samples_per_frame - 1); global->gain_lut[0][i] = cosf(t * (M_PI / 2.0f)); global->gain_lut[1][i] = sinf(t * (M_PI / 2.0f)); - global->fade_lut[i] = t; } - global->gain_lut[0][max_samples_per_frame - 1] = 0.0f; global->gain_lut[0][max_samples_per_frame - 2] = 0.0f; @@ -1933,6 +2003,7 @@ int vj_init_audio_fader_luts(veejay_t *info) { veejay_msg(VEEJAY_MSG_DEBUG, "[AudioMix] LUT[0] : out=%.3f in=%.3f (start)", global->gain_lut[0][0], global->gain_lut[1][0]); veejay_msg(VEEJAY_MSG_DEBUG, "[AudioMix] LUT[%d] : out=%.3f in=%.3f (mid)", mid, global->gain_lut[0][mid], global->gain_lut[1][mid]); veejay_msg(VEEJAY_MSG_DEBUG, "[AudioMix] LUT[%d] : out=%.3f in=%.3f (end)", last, global->gain_lut[0][last], global->gain_lut[1][last]); + float energy_mid = global->gain_lut[0][mid] * global->gain_lut[0][mid] + global->gain_lut[1][mid] * global->gain_lut[1][mid]; @@ -2286,10 +2357,15 @@ static void vj_perform_close_audio(performer_t *p) { int init_audio_resampler(veejay_t *info, performer_t *p) { #ifdef HAVE_JACK - const int chans = info->edit_list->audio_chans; - const int rate = info->edit_list->audio_rate; + editlist *el = vj_perform_audio_editlist(info); - p->audio_scratcher = vj_scratch_init( chans, rate, info->edit_list->video_fps ); + if(!vj_perform_audio_media_valid(el)) + return 1; + + const int chans = el->audio_chans; + const int rate = el->audio_rate; + + p->audio_scratcher = vj_scratch_init(chans, rate, el->video_fps); #endif return 1; } @@ -2307,7 +2383,13 @@ int vj_perform_init_audio(veejay_t * info, int AorB) } performer_global_t *global = (performer_global_t*) info->performer; - editlist *el = info->edit_list; + editlist *el = vj_perform_audio_editlist(info); + + if(!vj_perform_audio_media_valid(el)) { + veejay_msg(VEEJAY_MSG_WARNING, "[AUDIO] Skipping media audio buffers: no valid embedded audio stream"); + return 0; + } + performer_t *p = (AorB ? global->A : global->B); double samples_per_frame = (double)el->audio_rate / (double)el->video_fps; const uint32_t sample_len = ceil(samples_per_frame) * el->audio_bps; @@ -2568,54 +2650,85 @@ int vj_perform_preview_max_height(veejay_t *info) { int vj_perform_audio_start(veejay_t * info) { - editlist *el = info->edit_list; + editlist *el = info ? info->edit_list : NULL; - if (el->has_audio) - { -#ifdef HAVE_JACK - vj_jack_initialize(); - - int res = vj_jack_init_duplex(el); - - if( res <= 0 ) { - veejay_msg(VEEJAY_MSG_WARNING, - "[AUDIO] Jack duplex start failed; falling back to playback-only mode"); - - vj_jack_initialize(); - res = vj_jack_init(el); - } - - if( res <= 0 ) { - veejay_msg(0, "[AUDIO] Audio playback disabled"); - info->audio = NO_AUDIO; - return 0; - } - - if ( res == 2 ) - { - vj_jack_stop(); - info->audio = NO_AUDIO; - veejay_msg(VEEJAY_MSG_ERROR,"Please run jackd with a sample rate of %ld",el->audio_rate ); - return 0; - } - - if(vj_jack_has_input()) - veejay_msg(VEEJAY_MSG_DEBUG,"[AUDIO] Jack audio playback started with capture input ports"); - else - veejay_msg(VEEJAY_MSG_WARNING,"[AUDIO] Jack audio playback started without capture input ports"); - - return 1; -#else - veejay_msg(VEEJAY_MSG_WARNING, "[AUDIO] Jack support not compiled in (no audio)"); + if(!info || !el) + return 0; + +#ifdef HAVE_JACK + if(el->has_audio && !vj_perform_audio_params_valid(el)) { + veejay_msg(VEEJAY_MSG_WARNING, "[AUDIO] Embedded audio stream has invalid parameters; audio playback disabled"); return 0; -#endif } + + const int silent_output = !el->has_audio; + int saved_has_audio = el->has_audio; + + if(silent_output) { + if(!vj_perform_prepare_silence_audio_params(el)) { + veejay_msg(VEEJAY_MSG_WARNING, "[AUDIO] Unable to prepare silent playback parameters"); + return 0; + } + + if(info->current_edit_list && info->current_edit_list != el) + vj_perform_prepare_silence_audio_params(info->current_edit_list); + + veejay_msg(VEEJAY_MSG_INFO, + "[AUDIO] No embedded audio stream; starting JACK silence output (%ld Hz, %d channels, %d bit)", + (long)el->audio_rate, + el->audio_chans, + el->audio_bits); + el->has_audio = 1; + } + + vj_jack_initialize(); + + int res = vj_jack_init_duplex(el); + + if( res <= 0 ) { + veejay_msg(VEEJAY_MSG_WARNING, + "[AUDIO] Jack duplex start failed; falling back to playback-only mode"); + + vj_jack_initialize(); + res = vj_jack_init(el); + } + + if(silent_output) + el->has_audio = saved_has_audio; + + if( res <= 0 ) { + veejay_msg(0, "[AUDIO] Audio playback disabled"); + info->audio = NO_AUDIO; + return 0; + } + + if ( res == 2 ) + { + vj_jack_stop(); + info->audio = NO_AUDIO; + veejay_msg(VEEJAY_MSG_ERROR,"Please run jackd with a sample rate of %ld",(long)el->audio_rate ); + return 0; + } + + if(vj_jack_has_input()) + veejay_msg(VEEJAY_MSG_DEBUG,"[AUDIO] Jack audio playback started with capture input ports"); + else + veejay_msg(VEEJAY_MSG_WARNING,"[AUDIO] Jack audio playback started without capture input ports"); + + return 1; +#else + if(el->has_audio) + veejay_msg(VEEJAY_MSG_WARNING, "[AUDIO] Jack support not compiled in (no audio)"); return 0; +#endif } void vj_perform_audio_stop(veejay_t * info) { - if (info->edit_list->has_audio) { + if(!info) + return; + + if (info->audio == AUDIO_PLAY || (info->edit_list && info->edit_list->has_audio)) { #ifdef HAVE_JACK vj_jack_stop(); #endif