Working multidecoder using relative positioning in the frame buffer

This commit is contained in:
Jordan Sherer 2019-11-04 14:38:00 -05:00
parent 862e827ddf
commit bd357ed4c4
8 changed files with 161 additions and 45 deletions

View File

@ -2,7 +2,7 @@
#define COMMONS_H #define COMMONS_H
#define NSMAX 6827 #define NSMAX 6827
#define NTMAX 30 #define NTMAX 60
#define RX_SAMPLE_RATE 12000 #define RX_SAMPLE_RATE 12000
@ -13,6 +13,7 @@
#define JS8_DECODER_E2S 0 // decode every 2 seconds instead of at the half symbol stop #define JS8_DECODER_E2S 0 // decode every 2 seconds instead of at the half symbol stop
#define JS8_NUM_SYMBOLS 79 #define JS8_NUM_SYMBOLS 79
#define JS8_ENABLE_JS8A 1
#define JS8_ENABLE_JS8B 1 #define JS8_ENABLE_JS8B 1
#define JS8_ENABLE_JS8C 1 #define JS8_ENABLE_JS8C 1
#define JS8_ENABLE_JS8D 0 #define JS8_ENABLE_JS8D 0
@ -75,7 +76,6 @@ extern struct dec_data {
float ss[184*NSMAX]; // symbol spectra float ss[184*NSMAX]; // symbol spectra
float savg[NSMAX]; float savg[NSMAX];
float sred[5760]; float sred[5760];
short int d1[NTMAX*RX_SAMPLE_RATE]; // sample frame buffer for decoding (copied from d2 for the frame period)
short int d2[NTMAX*RX_SAMPLE_RATE]; // sample frame buffer for sample collection short int d2[NTMAX*RX_SAMPLE_RATE]; // sample frame buffer for sample collection
struct struct
{ {
@ -93,10 +93,17 @@ extern struct dec_data {
int nfb; //High decode limit (Hz) int nfb; //High decode limit (Hz)
int ntol; //+/- decoding range around fQSO (Hz) int ntol; //+/- decoding range around fQSO (Hz)
int kin; // number of frames written to d2 int kin; // number of frames written to d2
int kpos; // starting position of decode int kposA; // starting position of decode for submode A
int kout; // number of frames for decode int kposB; // starting position of decode for submode B
int kposC; // starting position of decode for submode C
int kposD; // starting position of decode for submode D
int kszA; // number of frames for decode for submode A
int kszB; // number of frames for decode for submode B
int kszC; // number of frames for decode for submode C
int kszD; // number of frames for decode for submode D
int nzhsym; // half symbol stop index int nzhsym; // half symbol stop index
int nsubmode; int nsubmode;
int nsubmodes;
bool nagain; bool nagain;
int ndepth; int ndepth;
bool lft8apon; bool lft8apon;

View File

@ -1,4 +1,4 @@
integer, parameter :: NTMAX=30 integer, parameter :: NTMAX=60
integer, parameter :: NMAX=NTMAX*12000 !Total sample intervals (one minute) integer, parameter :: NMAX=NTMAX*12000 !Total sample intervals (one minute)
integer, parameter :: NDMAX=NTMAX*1500 !Sample intervals at 1500 Hz rate integer, parameter :: NDMAX=NTMAX*1500 !Sample intervals at 1500 Hz rate
integer, parameter :: NSMAX=6827 !Max length of saved spectra integer, parameter :: NSMAX=6827 !Max length of saved spectra

View File

@ -1,4 +1,4 @@
subroutine multimode_decoder(ss,id1,params,nfsample) subroutine multimode_decoder(ss,id2,params,nfsample)
!$ use omp_lib !$ use omp_lib
use prog_args use prog_args
@ -28,8 +28,9 @@ subroutine multimode_decoder(ss,id1,params,nfsample)
end type counting_js8d_decoder end type counting_js8d_decoder
real ss(184,NSMAX) real ss(184,NSMAX)
logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,trydecode
integer*2 id1(NTMAX*12000) integer*2 id0(NTMAX*12000)
integer*2 id2(NTMAX*12000)
type(params_block) :: params type(params_block) :: params
character(len=20) :: datetime character(len=20) :: datetime
character(len=12) :: mycall, hiscall character(len=12) :: mycall, hiscall
@ -78,60 +79,92 @@ subroutine multimode_decoder(ss,id1,params,nfsample)
endif endif
endif endif
if(params%nmode.eq.8 .and. params%nsubmode.eq.4) then trydecode=.false.
if(params%nmode.eq.8 .and. (params%nsubmode.eq.4 .or. iand(params%nsubmodes, 8).eq.8)) then
! We're in JS8 mode D ! We're in JS8 mode D
call timer('decjs8d ',0) call timer('decjs8d ',0)
newdat=params%newdat newdat=params%newdat
call my_js8d%decode(js8d_decoded,id1,params%nQSOProgress,params%nfqso, & trydecode=.true.
! copy the relevant frames for decoding
pos = params%kposD
sz = params%kszD
id0=0
id0(1:sz+1)=id2(pos+1:pos+sz+1)
call my_js8d%decode(js8d_decoded,id0,params%nQSOProgress,params%nfqso, &
params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
params%nexp_decode,params%ndepth,logical(params%nagain), & params%nexp_decode,params%ndepth,logical(params%nagain), &
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
mycall,mygrid,hiscall,hisgrid) mycall,mygrid,hiscall,hisgrid)
call timer('decjs8d ',1) call timer('decjs8d ',1)
go to 800
endif endif
if(params%nmode.eq.8 .and. params%nsubmode.eq.2) then if(params%nmode.eq.8 .and. (params%nsubmode.eq.2 .or. iand(params%nsubmodes, 4).eq.4)) then
! We're in JS8 mode C ! We're in JS8 mode C
call timer('decjs8c ',0) call timer('decjs8c ',0)
newdat=params%newdat newdat=params%newdat
call my_js8c%decode(js8c_decoded,id1,params%nQSOProgress,params%nfqso, & trydecode=.true.
! copy the relevant frames for decoding
pos = params%kposC
sz = params%kszC
id0=0
id0(1:sz+1)=id2(pos+1:pos+sz+1)
call my_js8c%decode(js8c_decoded,id0,params%nQSOProgress,params%nfqso, &
params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
params%nexp_decode,params%ndepth,logical(params%nagain), & params%nexp_decode,params%ndepth,logical(params%nagain), &
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
mycall,mygrid,hiscall,hisgrid) mycall,mygrid,hiscall,hisgrid)
call timer('decjs8c ',1) call timer('decjs8c ',1)
go to 800
endif endif
if(params%nmode.eq.8 .and. params%nsubmode.eq.1) then if(params%nmode.eq.8 .and. (params%nsubmode.eq.1 .or. iand(params%nsubmodes, 2).eq.2)) then
! We're in JS8 mode B ! We're in JS8 mode B
call timer('decjs8b ',0) call timer('decjs8b ',0)
newdat=params%newdat newdat=params%newdat
call my_js8b%decode(js8b_decoded,id1,params%nQSOProgress,params%nfqso, & trydecode=.true.
! copy the relevant frames for decoding
pos = params%kposB
sz = params%kszB
id0=0
id0(1:sz+1)=id2(pos+1:pos+sz+1)
call my_js8b%decode(js8b_decoded,id0,params%nQSOProgress,params%nfqso, &
params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
params%nexp_decode,params%ndepth,logical(params%nagain), & params%nexp_decode,params%ndepth,logical(params%nagain), &
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
mycall,mygrid,hiscall,hisgrid) mycall,mygrid,hiscall,hisgrid)
call timer('decjs8b ',1) call timer('decjs8b ',1)
go to 800
endif endif
if(params%nmode.eq.8 .and. params%nsubmode.eq.0) then if(params%nmode.eq.8 .and. (params%nsubmode.eq.0 .or. iand(params%nsubmodes, 1).eq.1)) then
! We're in JS8 mode A ! We're in JS8 mode A
call timer('decjs8a ',0) call timer('decjs8a ',0)
newdat=params%newdat newdat=params%newdat
call my_js8a%decode(js8a_decoded,id1,params%nQSOProgress,params%nfqso, & trydecode=.true.
! copy the relevant frames for decoding
pos = params%kposA
sz = params%kszA
id0=0
id0(1:sz+1)=id2(pos+1:pos+sz+1)
call my_js8a%decode(js8a_decoded,id0,params%nQSOProgress,params%nfqso, &
params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, &
params%nexp_decode,params%ndepth,logical(params%nagain), & params%nexp_decode,params%ndepth,logical(params%nagain), &
logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, &
mycall,mygrid,hiscall,hisgrid) mycall,mygrid,hiscall,hisgrid)
call timer('decjs8a ',1) call timer('decjs8a ',1)
go to 800
endif endif
rms=sqrt(dot_product(float(id1(300000:310000)), & if(trydecode) go to 800
float(id1(300000:310000)))/10000.0)
rms=sqrt(dot_product(float(id2(300000:310000)), &
float(id2(300000:310000)))/10000.0)
if(rms.lt.2.0) go to 800 if(rms.lt.2.0) go to 800
! Zap data at start that might come from T/R switching transient? ! Zap data at start that might come from T/R switching transient?
@ -142,7 +175,7 @@ subroutine multimode_decoder(ss,id1,params,nfsample)
sq=0. sq=0.
do n=1,nadd do n=1,nadd
k=k+1 k=k+1
sq=sq + float(id1(k))**2 sq=sq + float(id2(k))**2
enddo enddo
rms=sqrt(sq/nadd) rms=sqrt(sq/nadd)
if(rms.gt.10000.0) then if(rms.gt.10000.0) then
@ -153,11 +186,11 @@ subroutine multimode_decoder(ss,id1,params,nfsample)
enddo enddo
if(bad0) then if(bad0) then
nz=min(NTMAX*12000,kbad+100) nz=min(NTMAX*12000,kbad+100)
! id1(1:nz)=0 ! temporarily disabled as it can breaak the JT9 decoder, maybe others ! id2(1:nz)=0 ! temporarily disabled as it can breaak the JT9 decoder, maybe others
endif endif
npts65=52*12000 npts65=52*12000
if(baddata(id1,npts65)) then if(baddata(id2,npts65)) then
nsynced=0 nsynced=0
ndecoded=0 ndecoded=0
go to 800 go to 800

View File

@ -297,6 +297,10 @@ program jt9
datetime="2013-Apr-16 15:13" !### Temp datetime="2013-Apr-16 15:13" !### Temp
shared_data%params%datetime=transfer(datetime,shared_data%params%datetime) shared_data%params%datetime=transfer(datetime,shared_data%params%datetime)
if(mode.eq.9 .and. fsplit.ne.2700) shared_data%params%nfa=fsplit if(mode.eq.9 .and. fsplit.ne.2700) shared_data%params%nfa=fsplit
shared_data%params%kszA=NMAX-1
shared_data%params%kszB=NMAX-1
shared_data%params%kszC=NMAX-1
shared_data%params%kszD=NMAX-1
call multimode_decoder(shared_data%ss,shared_data%id2,shared_data%params,nfsample) call multimode_decoder(shared_data%ss,shared_data%id2,shared_data%params,nfsample)
enddo enddo

View File

@ -61,7 +61,7 @@ subroutine jt9a()
local_params=shared_data%params !save a copy because wsjtx carries on accessing local_params=shared_data%params !save a copy because wsjtx carries on accessing
call flush(6) call flush(6)
call timer('decoder ',0) call timer('decoder ',0)
call multimode_decoder(shared_data%ss,shared_data%id1,local_params,12000) call multimode_decoder(shared_data%ss,shared_data%id2,local_params,12000)
call timer('decoder ',1) call timer('decoder ',1)
100 inquire(file=trim(temp_dir)//'/.lock',exist=fileExists) 100 inquire(file=trim(temp_dir)//'/.lock',exist=fileExists)

View File

@ -19,10 +19,17 @@
integer(c_int) :: nfb integer(c_int) :: nfb
integer(c_int) :: ntol integer(c_int) :: ntol
integer(c_int) :: kin integer(c_int) :: kin
integer(c_int) :: kpos integer(c_int) :: kposA
integer(c_int) :: kout integer(c_int) :: kposB
integer(c_int) :: kposC
integer(c_int) :: kposD
integer(c_int) :: kszA
integer(c_int) :: kszB
integer(c_int) :: kszC
integer(c_int) :: kszD
integer(c_int) :: nzhsym integer(c_int) :: nzhsym
integer(c_int) :: nsubmode integer(c_int) :: nsubmode
integer(c_int) :: nsubmodes
logical(c_bool) :: nagain logical(c_bool) :: nagain
integer(c_int) :: ndepth integer(c_int) :: ndepth
logical(c_bool) :: lft8apon logical(c_bool) :: lft8apon
@ -54,7 +61,6 @@
real(c_float) :: ss(184,NSMAX) real(c_float) :: ss(184,NSMAX)
real(c_float) :: savg(NSMAX) real(c_float) :: savg(NSMAX)
real(c_float) :: sred(5760) real(c_float) :: sred(5760)
integer(c_short) :: id1(NMAX)
integer(c_short) :: id2(NMAX) integer(c_short) :: id2(NMAX)
type(params_block) :: params type(params_block) :: params
end type dec_data end type dec_data

View File

@ -1015,6 +1015,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
if(pProcessed) *pProcessed = true; if(pProcessed) *pProcessed = true;
}); });
ui->modeButton->installEventFilter(mbmp); ui->modeButton->installEventFilter(mbmp);
if(!JS8_ENABLE_JS8A){
ui->actionModeJS8Normal->setVisible(false);
}
if(!JS8_ENABLE_JS8B){ if(!JS8_ENABLE_JS8B){
ui->actionModeJS8Fast->setVisible(false); ui->actionModeJS8Fast->setVisible(false);
} }
@ -2480,7 +2483,7 @@ int MainWindow::computeStop(int submode, int period){
case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; /* threshold = 1.00 */ ; break; case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; /* threshold = 1.00 */ ; break;
case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; /* threshold = 1.08 */ ; break; case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; /* threshold = 1.08 */ ; break;
case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; /* threshold = 0.50 */ ; break; case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; /* threshold = 0.50 */ ; break;
case Varicode::JS8CallUltra: symbolSamples = JS8D_SYMBOL_SAMPLES; /* threshold = 0.00 */ ; break; case Varicode::JS8CallUltra: symbolSamples = JS8D_SYMBOL_SAMPLES; threshold = 0.00; break;
} }
stop = qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE)/(float)m_nsps*2.0); stop = qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE)/(float)m_nsps*2.0);
#endif #endif
@ -2580,6 +2583,14 @@ void MainWindow::dataSink(qint64 frames)
dec_data.params.nagain=0; dec_data.params.nagain=0;
dec_data.params.nzhsym=m_ihsym; dec_data.params.nzhsym=m_ihsym;
// only decode once per second
static int lastn = -1;
int n = m_detector->secondInPeriod();
if(n == lastn){
return;
}
lastn = n;
decode(); decode();
} }
@ -3845,7 +3856,7 @@ void MainWindow::decode(){
qint32 submode = m_nSubMode; qint32 submode = m_nSubMode;
qint32 period = m_TRperiod; qint32 period = m_TRperiod;
bool ready = decodeReady(submode, period); bool ready = decodeReady(submode, period, &submode, &period);
if(!ready){ if(!ready){
return; return;
} }
@ -3854,28 +3865,80 @@ void MainWindow::decode(){
decodePrepareSaveAudio(submode, period); decodePrepareSaveAudio(submode, period);
} }
bool MainWindow::decodeReady(int submode, int period){ bool MainWindow::decodeReady(int submode, int period, int *pSubmode, int *pPeriod){
if(period == 0){ if(period == 0){
return false; return false;
} }
#if JS8_RING_BUFFER #if JS8_RING_BUFFER
qint32 cycle = computeCurrentCycle(period); int k = dec_data.params.kin;
qint32 cycleSampleStart = computeCycleStartForDecode(cycle, period);
qint32 framesNeeded = computeFramesNeededForDecode(submode, period);
static int lastCycle = -1; qint32 cycleSampleStartA = computeCycleStartForDecode(computeCurrentCycle(JS8A_TX_SECONDS), JS8A_TX_SECONDS);
if(cycle < lastCycle){ qint32 framesNeededA = computeFramesNeededForDecode(Varicode::JS8CallNormal, JS8A_TX_SECONDS);
lastCycle = -1; bool couldDecodeA = k >= cycleSampleStartA + framesNeededA;
qint32 cycleSampleStartB = computeCycleStartForDecode(computeCurrentCycle(JS8B_TX_SECONDS), JS8B_TX_SECONDS);
qint32 framesNeededB = computeFramesNeededForDecode(Varicode::JS8CallFast, JS8B_TX_SECONDS);
bool couldDecodeB = k >= cycleSampleStartB + framesNeededB;
qint32 cycleSampleStartC = computeCycleStartForDecode(computeCurrentCycle(JS8C_TX_SECONDS), JS8C_TX_SECONDS);
qint32 framesNeededC = computeFramesNeededForDecode(Varicode::JS8CallTurbo, JS8C_TX_SECONDS);
bool couldDecodeC = k >= cycleSampleStartC + framesNeededC;
#if JS8_ENABLE_JS8D
qint32 cycleSampleStartD = computeCycleStartForDecode(computeCurrentCycle(JS8D_TX_SECONDS), JS8D_TX_SECONDS);
qint32 framesNeededD = computeFramesNeededForDecode(Varicode::JS8CallUltra, JS8D_TX_SECONDS);
bool couldDecodeD = k >= cycleSampleStartD + framesNeededD;
#else
qint32 cycleSampleStartD = 0;
qint32 framesNeededD = 0;
bool couldDecodeD = false;
#endif
// set the params for starting positions and sizes for decode
dec_data.params.kposA = cycleSampleStartA;
dec_data.params.kposB = cycleSampleStartB;
dec_data.params.kposC = cycleSampleStartC;
dec_data.params.kposD = cycleSampleStartD;
dec_data.params.kszA = qMax(framesNeededA, k-cycleSampleStartA);
dec_data.params.kszB = qMax(framesNeededB, k-cycleSampleStartB);
dec_data.params.kszC = qMax(framesNeededC, k-cycleSampleStartC);
dec_data.params.kszD = qMax(framesNeededD, k-cycleSampleStartD);
dec_data.params.nsubmodes = 0;
int decodes = int(couldDecodeA) + int(couldDecodeB) + int(couldDecodeC) + int(couldDecodeD);
if(couldDecodeD){
qDebug() << "could decode D from" << cycleSampleStartD << "to" << cycleSampleStartD + framesNeededD;
submode = Varicode::JS8CallUltra;
period = JS8D_TX_SECONDS;
dec_data.params.nsubmodes |= (Varicode::JS8CallUltra << 1);
}
if(couldDecodeC){
qDebug() << "could decode C from" << cycleSampleStartC << "to" << cycleSampleStartC + framesNeededC;
submode = Varicode::JS8CallTurbo;
period = JS8C_TX_SECONDS;
dec_data.params.nsubmodes |= (Varicode::JS8CallTurbo << 1);
}
if(couldDecodeB){
qDebug() << "could decode B from" << cycleSampleStartB << "to" << cycleSampleStartB + framesNeededB;
submode = Varicode::JS8CallFast;
period = JS8B_TX_SECONDS;
dec_data.params.nsubmodes |= (Varicode::JS8CallFast << 1);
}
if(couldDecodeA){
qDebug() << "could decode A from" << cycleSampleStartA << "to" << cycleSampleStartA + framesNeededA;
submode = Varicode::JS8CallNormal;
period = JS8A_TX_SECONDS;
dec_data.params.nsubmodes |= (Varicode::JS8CallNormal + 1);
} }
int k = dec_data.params.kin; if(pSubmode) *pSubmode=submode;
if(!m_diskData && (cycle == lastCycle || k < cycleSampleStart + framesNeeded)){ if(pPeriod) *pPeriod=period;
if(!m_diskData && decodes == 0){
return false; return false;
} }
qDebug() << "cycle" << cycle << "start" << cycleSampleStart << "k" << k << "needed" << framesNeeded;
lastCycle = cycle;
#endif #endif
#if JS8_DECODER_E2S #if JS8_DECODER_E2S
@ -4011,6 +4074,7 @@ bool MainWindow::decodeReady(int submode, int period){
strncpy(dec_data.params.hiscall,(hisCall + " ").toLatin1 ().constData (), 12); strncpy(dec_data.params.hiscall,(hisCall + " ").toLatin1 ().constData (), 12);
strncpy(dec_data.params.hisgrid,(hisGrid + " ").toLatin1 ().constData (), 6); strncpy(dec_data.params.hisgrid,(hisGrid + " ").toLatin1 ().constData (), 6);
#if JS8_TWO_BUFFERS
#if JS8_RING_BUFFER #if JS8_RING_BUFFER
// clear out d1 // clear out d1
memset(dec_data.d1, 0, sizeof(dec_data.d1)); memset(dec_data.d1, 0, sizeof(dec_data.d1));
@ -4047,6 +4111,7 @@ bool MainWindow::decodeReady(int submode, int period){
qDebug() << "try decode from" << 0 << "to" << dec_data.params.kin; qDebug() << "try decode from" << 0 << "to" << dec_data.params.kin;
memset(dec_data.d1, 0, sizeof(dec_data.d1)); memset(dec_data.d1, 0, sizeof(dec_data.d1));
memcpy(dec_data.d1, dec_data.d2, sizeof(dec_data.d2)); memcpy(dec_data.d1, dec_data.d2, sizeof(dec_data.d2));
#endif
#endif #endif
return true; return true;
@ -7066,7 +7131,8 @@ void MainWindow::on_actionJS8_triggered()
m_wideGraph->setPeriod(m_TRperiod, m_nsps); m_wideGraph->setPeriod(m_TRperiod, m_nsps);
m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe
#if JS8_RING_BUFFER #if JS8_RING_BUFFER
m_detector->setTRPeriod(NTMAX); // TODO - not thread safe Q_ASSERT(NTMAX == 60);
m_detector->setTRPeriod(NTMAX / 2); // TODO - not thread safe
#else #else
m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe
#endif #endif

View File

@ -230,7 +230,7 @@ private slots:
void on_actionCopyright_Notice_triggered(); void on_actionCopyright_Notice_triggered();
void on_DecodeButton_clicked (bool); void on_DecodeButton_clicked (bool);
void decode(); void decode();
bool decodeReady(int submode, int period); bool decodeReady(int submode, int period, int *pSubmode, int *pPeriod);
void decodeStart(int submode, int period); void decodeStart(int submode, int period);
void decodePrepareSaveAudio(int submode, int period); void decodePrepareSaveAudio(int submode, int period);
void decodeBusy(bool b); void decodeBusy(bool b);