Rivendellaudio/lib/rdplay_deck.cpp
Fred Gleason e15c96481a 2021-07-20 Fred Gleason <fredg@paravelsystems.com>
* Fixed a bug in rdlogedit(1) that could cause a segfault when
	attempting to play a non-audio event in the 'Voice Tracker' dialog.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
2021-07-20 16:28:53 -04:00

779 lines
19 KiB
C++

// rdplay_deck.cpp
//
// Abstract a Rivendell Playback Deck
//
// (C) Copyright 2003-2004,2016 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <qsignalmapper.h>
#include <rdplay_deck.h>
#include <rdmixer.h>
RDPlayDeck::RDPlayDeck(RDCae *cae,int id,QObject *parent)
: QObject(parent)
{
play_id=id;
play_state=RDPlayDeck::Stopped;
play_start_time=QTime();
play_owner=-1;
play_last_start_position=0;
play_handle=-1;
play_audio_length=0;
play_channel=-1;
play_hook_mode=false;
play_cut_gain=0;
play_duck_level=0;
play_duck_gain[0]=0;
play_duck_gain[1]=0;
play_duck_up=RDPLAYDECK_DUCKUP_LENGTH;
play_duck_down=RDPLAYDECK_DUCKDOWN_LENGTH;
play_duck_up_point=0;
play_duck_down_state=false;
play_fade_down_state=false;
//
// CAE Connection
//
play_cae=cae;
connect(play_cae,SIGNAL(playing(int)),this,SLOT(playingData(int)));
connect(play_cae,SIGNAL(playStopped(int)),this,SLOT(playStoppedData(int)));
play_cart=NULL;
play_cut=NULL;
play_card=-1;
play_stream=-1;
//
// Timers
//
QSignalMapper *mapper=new QSignalMapper(this,"timer_mapper");
connect(mapper,SIGNAL(mapped(int)),this,SLOT(pointTimerData(int)));
for(int i=0;i<3;i++) {
play_point_timer[i]=new QTimer(this,"point_timer");
connect(play_point_timer[i],SIGNAL(timeout()),mapper,SLOT(map()));
mapper->setMapping(play_point_timer[i],i);
}
play_position_timer=new QTimer(this,"play_position_timer");
connect(play_position_timer,SIGNAL(timeout()),
this,SLOT(positionTimerData()));
play_fade_timer=new QTimer(this,"play_fade_timer");
connect(play_fade_timer,SIGNAL(timeout()),this,SLOT(fadeTimerData()));
play_stop_timer=new QTimer(this,"play_stop_timer");
connect(play_stop_timer,SIGNAL(timeout()),this,SLOT(stop()));
play_duck_timer=new QTimer(this,"play_duck_timer");
connect(play_duck_timer,SIGNAL(timeout()),this,SLOT(duckTimerData()));
}
RDPlayDeck::~RDPlayDeck()
{
if(play_state!=RDPlayDeck::Stopped) {
play_cae->stopPlay(play_handle);
play_cae->unloadPlay(play_handle);
}
}
int RDPlayDeck::id() const
{
return play_id;
}
void RDPlayDeck::setId(int id)
{
play_id=id;
}
int RDPlayDeck::owner() const
{
return play_owner;
}
void RDPlayDeck::setOwner(int owner)
{
play_owner=owner;
}
RDCart *RDPlayDeck::cart() const
{
return play_cart;
}
bool RDPlayDeck::setCart(RDLogLine *logline,bool rotate)
{
play_timescale_active=logline->timescalingActive();
if((play_cart!=NULL)&&(rotate||play_cart->number()!=logline->cartNumber())) {
delete play_cart;
delete play_cut;
play_cart=NULL;
play_cut=NULL;
}
if(play_cart==NULL) {
StopTimers();
play_cart=new RDCart(logline->cartNumber());
if(!play_cart->exists()) {
delete play_cart;
play_cart=NULL;
return false;
}
QString cutname=logline->cutName();
//
// FIXME: We need to handle the 'cut no longer valid' case better!
//
//if(play_cart->selectCut(&cutname)) { This fixes problems with cuts of different length in one cart.
// logline->setCutName(cutname); We do not need to select a cut, because it is done immediatly before
//} this method is called.
if(cutname.isEmpty()) {
return false;
}
play_cut=new RDCut(cutname);
if(!play_cut->exists()) {
delete play_cut;
play_cut=NULL;
return false;
}
}
if(logline->startPoint(RDLogLine::LogPointer)<0) {
play_forced_length=logline->forcedLength();
/*
play_audio_point[0]=logline->startPoint();
play_audio_point[1]=logline->endPoint();
*/
play_audio_point[0]=play_cut->startPoint(RDLogLine::CartPointer);
play_audio_point[1]=play_cut->endPoint();
}
else {
play_forced_length=logline->effectiveLength();
play_audio_point[0]=logline->startPoint(RDLogLine::LogPointer);
play_audio_point[1]=logline->endPoint();
}
if(logline->endPoint(RDLogLine::LogPointer)>=0) {
play_forced_length=logline->effectiveLength();
play_audio_point[0]=logline->startPoint();
play_audio_point[1]=logline->endPoint(RDLogLine::LogPointer);
}
if(play_timescale_active) {
play_timescale_speed=
(int)(RD_TIMESCALE_DIVISOR*(double)(play_audio_point[1]-
play_audio_point[0])/
(double)play_forced_length);
if((((double)play_timescale_speed)<
(RD_TIMESCALE_DIVISOR*RD_TIMESCALE_MIN))||
(((double)play_timescale_speed)>
(RD_TIMESCALE_DIVISOR*RD_TIMESCALE_MAX))) {
play_timescale_speed=(int)RD_TIMESCALE_DIVISOR;
play_timescale_active=false;
}
}
else {
play_timescale_speed=(int)RD_TIMESCALE_DIVISOR;
}
play_audio_length=play_audio_point[1]-play_audio_point[0];
if(logline->segueStartPoint(RDLogLine::AutoPointer)<0) {
play_point_value[RDPlayDeck::Segue][0]=
(int)((double)play_cut->segueStartPoint());
play_point_value[RDPlayDeck::Segue][1]=
(int)((double)play_cut->segueEndPoint());
}
else {
play_point_value[RDPlayDeck::Segue][0]=
(int)((double)logline->segueStartPoint(RDLogLine::AutoPointer));
play_point_value[RDPlayDeck::Segue][1]=
(int)((double)logline->segueEndPoint(RDLogLine::AutoPointer));
}
play_point_gain=logline->segueGain();
play_point_value[RDPlayDeck::Hook][0]=
(int)((double)play_cut->hookStartPoint());
play_point_value[RDPlayDeck::Hook][1]=
(int)((double)play_cut->hookEndPoint());
logline->setHookStartPoint(play_point_value[RDPlayDeck::Hook][0]);
logline->setHookEndPoint(play_point_value[RDPlayDeck::Hook][1]);
play_point_value[RDPlayDeck::Talk][0]=
(int)((double)play_cut->talkStartPoint()*
(RD_TIMESCALE_DIVISOR/(double)play_timescale_speed));
play_point_value[RDPlayDeck::Talk][1]=
(int)((double)play_cut->talkEndPoint()*
(RD_TIMESCALE_DIVISOR/(double)play_timescale_speed));
logline->setTalkStartPoint(play_point_value[RDPlayDeck::Talk][0]);
logline->setTalkEndPoint(play_point_value[RDPlayDeck::Talk][1]);
if(logline->fadeupPoint(RDLogLine::LogPointer)<0) {
play_fade_point[0]=play_cut->fadeupPoint();
play_fade_gain[0]=RD_FADE_DEPTH;
}
else {
play_fade_point[0]=logline->fadeupPoint(RDLogLine::LogPointer);
play_fade_gain[0]=logline->fadeupGain();
}
if(logline->fadedownPoint(RDLogLine::LogPointer)<0) {
play_fade_point[1]=play_cut->fadedownPoint();
play_fade_gain[1]=RD_FADE_DEPTH;
}
else {
play_fade_point[1]=logline->fadedownPoint(RDLogLine::LogPointer);
play_fade_gain[1]=logline->fadedownGain();
}
play_duck_gain[0]=logline->duckUpGain();
play_duck_gain[1]=logline->duckDownGain();
/*
if(play_timescale_active) {
play_timescale_speed=
(int)(1000*(double)(play_audio_point[1]-play_audio_point[0])/
(double)play_forced_length);
}
else {
play_timescale_speed=1000;
}
*/
if(play_state!=RDPlayDeck::Paused) {
if(!play_cae->loadPlay(play_card,play_cut->cutName(),
&play_stream,&play_handle)) {
return false;
}
}
play_state=RDPlayDeck::Stopped;
return true;
}
RDCut *RDPlayDeck::cut() const
{
return play_cut;
}
bool RDPlayDeck::playable() const
{
if(play_handle<0) {
return false;
}
return true;
}
int RDPlayDeck::card() const
{
return play_card;
}
void RDPlayDeck::setCard(int card_num)
{
play_card=card_num;
}
int RDPlayDeck::stream() const
{
return play_stream;
}
int RDPlayDeck::port() const
{
return play_port;
}
void RDPlayDeck::setPort(int port_num)
{
play_port=port_num;
}
int RDPlayDeck::channel() const
{
return play_channel;
}
void RDPlayDeck::setChannel(int chan)
{
play_channel=chan;
}
RDPlayDeck::State RDPlayDeck::state() const
{
return play_state;
}
QTime RDPlayDeck::startTime() const
{
return play_start_time;
}
int RDPlayDeck::currentPosition() const
{
switch(play_state) {
case RDPlayDeck::Playing:
return play_start_position+
play_start_time.msecsTo(QTime::currentTime());
case RDPlayDeck::Paused:
return play_current_position+POSITION_INTERVAL;
default:
return play_start_position;
}
return 0;
}
int RDPlayDeck::lastStartPosition() const
{
return play_last_start_position;
}
void RDPlayDeck::clear()
{
StopTimers();
switch(play_state) {
case RDPlayDeck::Playing:
case RDPlayDeck::Stopping:
stop();
break;
case RDPlayDeck::Paused:
play_cae->unloadPlay(play_handle);
emit stateChanged(play_id,RDPlayDeck::Stopped);
break;
default:
emit stateChanged(play_id,RDPlayDeck::Stopped);
break;
}
}
void RDPlayDeck::reset()
{
StopTimers();
switch(play_state) {
case RDPlayDeck::Playing:
case RDPlayDeck::Stopping:
play_cae->stopPlay(play_handle);
case RDPlayDeck::Paused:
play_cae->unloadPlay(play_handle);
break;
default:
break;
}
play_state=RDPlayDeck::Stopped;
}
void RDPlayDeck::play(unsigned pos,int segue_start,int segue_end,
int duck_up_end)
{
if(play_cut==NULL) {
return;
}
int fadeup;
play_hook_mode=false;
play_cut_gain=play_cut->playGain();
play_ducked=0;
if(duck_up_end==-1) { //ducked until stop (for recording in voice tracker)
play_ducked=play_duck_gain[0];
play_duck_up_point=0;
}
else {
play_duck_up_point=duck_up_end-play_duck_up;
}
if(play_duck_up_point<0)
play_duck_up_point=0;
else
play_ducked=play_duck_gain[0];
if(play_handle<0) {
return;
}
if(segue_start>=0) {
play_point_value[RDPlayDeck::Segue][0]=segue_start;
}
if(segue_end>=0) {
play_point_value[RDPlayDeck::Segue][1]=segue_end;
}
play_start_position=pos;
play_current_position=pos;
play_last_start_position=play_start_position;
stop_called=false;
pause_called=false;
play_cae->positionPlay(play_handle,play_audio_point[0]+pos);
play_cae->setPlayPortActive(play_card,play_port,play_stream);
for(int i=0;i<RD_MAX_PORTS;i++) {
play_cae->setOutputVolume(play_card,play_stream,i,RD_MUTE_DEPTH);
}
if((play_fade_point[0]==-1)||(play_fade_point[0]==play_audio_point[0])||
((fadeup=play_fade_point[0]-play_audio_point[0]-pos)<=0)||
(play_state==RDPlayDeck::Paused)) {
if((play_fade_point[1]==-1)||((fadeup=pos-play_fade_point[1])<=0)||
(play_state==RDPlayDeck::Paused)) {
play_cae->setOutputVolume(play_card,play_stream,play_port,
play_ducked+play_cut_gain+play_duck_level);
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_ducked+play_cut_gain+play_duck_level,10);
}
else { // Fadedown event in progress, interpolate the gain accordingly
int level=play_fade_gain[1]*((int)pos-play_fade_point[1])/
(play_audio_point[1]-play_fade_point[1]);
play_cae->
setOutputVolume(play_card,play_stream,play_port,
level+play_cut_gain+play_duck_level);
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_fade_gain[1]+play_cut_gain+
play_duck_level,
play_audio_point[1]-(int)pos);
}
}
else { // FadeUp event in progress, interpolate the gain accordingly
int level=(play_fade_gain[0]*fadeup/
(play_fade_point[0]-play_audio_point[0]));
if (level>play_ducked) {
play_cae->
setOutputVolume(play_card,play_stream,play_port,
play_ducked+play_cut_gain+play_duck_level);
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_ducked+play_cut_gain+play_duck_level,
fadeup);
}
else {
play_cae->
setOutputVolume(play_card,play_stream,play_port,
level+play_cut_gain+play_duck_level);
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_ducked+play_cut_gain+play_duck_level,
fadeup);
}
}
play_cae->
play(play_handle,
(int)(100000.0*(double)(play_audio_point[1]-play_audio_point[0]-pos)/
(double)play_timescale_speed),
play_timescale_speed,false);
play_start_time=QTime::currentTime();
StartTimers(pos);
play_state=RDPlayDeck::Playing;
}
void RDPlayDeck::playHook()
{
play(play_point_value[RDPlayDeck::Hook][0]-play_audio_point[0]);
play_hook_mode=true;
}
void RDPlayDeck::pause()
{
pause_called=true;
play_state=RDPlayDeck::Paused;
play_cae->stopPlay(play_handle);
}
void RDPlayDeck::stop()
{
if((play_state!=RDPlayDeck::Playing)&&(play_state!=RDPlayDeck::Stopping)) {
return;
}
if(pause_called) {
play_state=RDPlayDeck::Stopped;
}
else {
stop_called=true;
play_state=RDPlayDeck::Stopping;
play_cae->stopPlay(play_handle);
}
}
void RDPlayDeck::stop(int interval,int gain)
{
int level;
if(gain>play_point_gain) {
play_point_gain=gain;
}
if((play_state!=RDPlayDeck::Playing)&&(play_state!=RDPlayDeck::Stopping)) {
return;
}
if((interval<=0)||pause_called) {
stop();
}
else {
if(play_duck_gain[1]<0 && play_duck_down<interval &&
(play_audio_point[1]-play_audio_point[0]-
currentPosition())>play_duck_down) { // duck
if(play_audio_point[0]+currentPosition()>play_fade_point[1]) {
level=play_fade_gain[1]*((currentPosition()+play_audio_point[0])-
play_fade_point[1])/(play_audio_point[1]-play_fade_point[1]);
}
else {
level=0;
}
if(level>play_duck_gain[1]){
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_duck_gain[1]+play_cut_gain+play_duck_level,play_duck_down);
play_duck_timer->start(play_duck_down,true);
play_duck_down_state=true;
play_segue_interval=interval;
}
}
else {
if(play_point_gain!=0) {
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_point_gain+play_cut_gain+play_duck_level,interval);
}
}
play_stop_timer->start(interval,true);
stop_called=true;
play_state=RDPlayDeck::Stopping;
}
}
void RDPlayDeck::duckDown(int interval)
{
if(play_duck_gain[1]<0) {
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_duck_gain[1]+play_cut_gain+play_duck_level,play_duck_down);
play_duck_timer->start(play_duck_down,true);
play_duck_down_state=true;
play_segue_interval=interval;
}
}
void RDPlayDeck::duckVolume(int level,int fade)
{
play_duck_level=level;
if((state()==RDPlayDeck::Playing || state()==RDPlayDeck::Stopping) && fade>0) {
play_cae->fadeOutputVolume(play_card,play_stream,play_port,play_cut_gain+play_duck_level,
fade);
}
}
void RDPlayDeck::playingData(int handle)
{
if(handle!=play_handle) {
return;
}
play_position_timer->start(POSITION_INTERVAL);
emit stateChanged(play_id,RDPlayDeck::Playing);
}
void RDPlayDeck::playStoppedData(int handle)
{
if(handle!=play_handle) {
return;
}
play_position_timer->stop();
play_start_time=QTime();
StopTimers();
if(pause_called) {
play_state=RDPlayDeck::Paused;
emit stateChanged(play_id,RDPlayDeck::Paused);
}
else {
play_cae->unloadPlay(play_handle);
play_handle=-1;
play_state=RDPlayDeck::Stopped;
play_current_position=0;
play_duck_down_state=false;
play_fade_down_state=false;
if(stop_called) {
emit stateChanged(play_id,RDPlayDeck::Stopped);
}
else {
emit stateChanged(play_id,RDPlayDeck::Finished);
}
}
}
void RDPlayDeck::pointTimerData(int point)
{
switch(point) {
case RDPlayDeck::Segue:
if(play_point_state[point]) {
play_point_state[point]=false;
emit segueEnd(play_id);
}
else {
play_point_state[point]=true;
play_point_timer[point]->
start(play_point_value[point][1]-play_point_value[point][0],true);
emit segueStart(play_id);
}
break;
case RDPlayDeck::Hook:
if(play_point_state[point]) {
play_point_state[point]=false;
emit hookEnd(play_id);
}
else {
play_point_state[point]=true;
play_point_timer[point]->
start(play_point_value[point][1]-play_point_value[point][0],true);
emit hookStart(play_id);
}
break;
case RDPlayDeck::Talk:
if(play_point_state[point]) {
play_point_state[point]=false;
emit talkEnd(play_id);
}
else {
play_point_state[point]=true;
play_point_timer[point]->
start(play_point_value[point][1]-play_point_value[point][0],true);
emit talkStart(play_id);
}
break;
}
}
void RDPlayDeck::positionTimerData()
{
play_current_position=
play_start_position+play_start_time.msecsTo(QTime::currentTime());
if(play_current_position<0) { // Handle crossing midnight!
play_current_position+=86400000;
}
if(play_hook_mode) {
emit position(play_id,play_current_position-(play_point_value[RDPlayDeck::Hook][0]-play_audio_point[0]));
}
else {
emit position(play_id,play_current_position);
}
}
void RDPlayDeck::fadeTimerData()
{
if(!play_duck_down_state) {
play_cae->
fadeOutputVolume(play_card,play_stream,play_port,play_fade_gain[1]+play_cut_gain+play_duck_level,
play_fade_down);
}
play_fade_down_state=true;
}
void RDPlayDeck::duckTimerData()
{
if (!play_duck_down_state) { //duck up
play_cae->
fadeOutputVolume(play_card,play_stream,play_port,0+play_cut_gain+play_duck_level,play_duck_up);
play_ducked=0;
}
else { //duck down
if(play_point_gain!=0) {
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_point_gain+play_cut_gain+play_duck_level,
play_segue_interval-play_duck_down);
}
else {
if(play_fade_down_state &&
play_fade_gain[1]<play_duck_gain[1]) { //fade down in progress
play_cae->fadeOutputVolume(play_card,play_stream,play_port,
play_fade_gain[1]+play_cut_gain+play_duck_level,
play_segue_interval-play_duck_down);
}
}
play_duck_down_state=false;
}
}
void RDPlayDeck::StartTimers(int offset)
{
int audio_point;
for(int i=0;i<RDPlayDeck::SizeOf;i++) {
play_point_state[i]=false;
if(play_point_value[i][0]!=-1) {
audio_point=(int)
(RD_TIMESCALE_DIVISOR*(double)play_audio_point[0]/
(double)play_timescale_speed);
if((play_point_value[i][0]-audio_point-offset)>=0) {
play_point_timer[i]->
start(play_point_value[i][0]-audio_point-offset,true);
}
else {
if((play_point_value[i][1]-audio_point-offset)>=0) {
play_point_state[i]=true;
play_point_timer[i]->
start(play_point_value[i][1]-audio_point-offset,true);
}
}
}
}
if((play_fade_point[1]!=-1)&&(offset<play_fade_point[1])&&
((play_fade_down=play_audio_point[1]-play_fade_point[1])>0)) {
play_fade_timer->start(play_fade_point[1]-play_audio_point[0]-offset,true);
}
if(offset<play_duck_up_point){
play_duck_timer->
start(play_duck_up_point-offset,true);
}
}
void RDPlayDeck::StopTimers()
{
for(int i=0;i<RDPlayDeck::SizeOf;i++) {
if(play_point_timer[i]->isActive()) {
play_point_timer[i]->stop();
}
}
if(play_fade_timer->isActive()) {
play_fade_timer->stop();
}
if(play_stop_timer->isActive()) {
play_stop_timer->stop();
}
if(play_duck_timer->isActive()) {
play_duck_timer->stop();
}
}