Logo Search packages:      
Sourcecode: tucnak2 version File versions  Download package

ssbd.c

/*
    Tucnak - VHF contest log
    Copyright (C) 2002-2006  Ladislav Vaiz <ok1zia@nagano.cz>

    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.

*/

#include "header.h"

struct ssbd *ssbd;
GMutex *ssbd_mutex;


struct ssbd *init_ssbd(){
    struct ssbd *ssbd;

    ssbd = g_new0(struct ssbd, 1);
    ssbd_mutex = g_mutex_new();    
    
#ifdef HAVE_SDL    
    ssbd->norecicon = do_png_create(icon_norec, sizeof(icon_norec));
    if (!ssbd->norecicon) internal("Can't create norec icon, currupted executable?");
    
    ssbd->recicon = do_png_create(icon_record, sizeof(icon_record));
    if (!ssbd->recicon) internal("Can't create record icon, currupted executable?");
    
    ssbd->playicon = do_png_create(icon_play, sizeof(icon_play));
    if (!ssbd->playicon) internal("Can't create play icon, currupted executable?");
#endif
    
    return ssbd;
    
}

void free_ssbd(struct ssbd *ssbd){
    cq_abort(1);
    ssbd_abort(ssbd,1); 
    ssbd_thread_kill(ssbd); /* killed before but... */
    CONDGFREE(ssbd->callsign);
    CONDGFREE(ssbd->pfilename);
    CONDGFREE(ssbd->rfilename);
#ifdef HAVE_SDL    
    if (ssbd->playicon) SDL_FreeSurface(ssbd->playicon);
    if (ssbd->recicon) SDL_FreeSurface(ssbd->recicon);
    if (ssbd->norecicon) SDL_FreeSurface(ssbd->norecicon);
#endif
    g_mutex_free(ssbd_mutex);
    g_free(ssbd);
}

void ssbd_abort(struct ssbd *ssbd, int abort_rec){
    gchar *tmpfile;
    
    /*dbg("ssbd_abort recording=%d abort_rec=%d\n", ssbd->recording, abort_rec);*/
    
    if (!ssbd) return;
    
    if (gses) gses->icon=NULL;
    ssbd_thread_kill(ssbd);
        
    if (ssbd->sndfile) { 
        sf_close (ssbd->sndfile);
        ssbd->sndfile = NULL;
    
        if (!cfg->ssbd_template || !ssbd->rfilename) goto x;
        tmpfile=convert_esc(cfg->ssbd_template, NULL, CE_NONE);
        /*dbg("cfg->template='%s' cfg->callsign='%s' \n", cfg->ssbd_template, ssbd->callsign);
        dbg("tmpfile='%s'  cfg->filename='%s'\n", tmpfile, ssbd->rfilename);*/
        if (strcmp(tmpfile, ssbd->rfilename)!=0){
            dbg("renaming '%s' to '%s'\n", ssbd->rfilename, tmpfile);         
            rename(ssbd->rfilename, tmpfile);
            g_free(ssbd->rfilename);
            ssbd->rfilename=g_strdup(tmpfile);
            
        }
        g_free(tmpfile);
    }
x:;    
    dsp->close(dsp);
 
    ssbd->recording=0;
    if (!abort_rec) ssbd_rec_file(ssbd);
}

void ssbd_thread_create(struct ssbd *ssbd, GThreadFunc thread_func){
    if (ssbd->pid) raise(SIGSEGV);
    /*dbg("ssbd_thread_create");*/
 /*   if (thread_func==ssbd_play_thread_func) dbg(" play"); 
    else if (thread_func==ssbd_rec_thread_func) dbg(" rec");
    else dbg(" ???");                          
   */ 
  /*  dbg("\n");*/
    ssbd->proc_break=0;
    ssbd->pid=0;
    ssbd->pid=fork();
    if (!ssbd->pid){
        thread_func(NULL);
        exit(0);
    }
    if (ssbd->pid<0) raise(SIGSEGV);
    dbg("created thread %d\n", ssbd->pid);
}

void ssbd_thread_join(struct ssbd *ssbd){
    int status;
    
    if (!ssbd->pid) return;
   /** dbg("ssbd_thread_join...\n");*/
    waitpid(ssbd->pid, &status, 0);
    /*dbg("done\n");*/
    ssbd->pid=0;
}

void ssbd_thread_kill(struct ssbd *ssbd){
    int status;

    if (!ssbd->pid) return;
    ST_START;
    if (ssbd->pid) kill(ssbd->pid, SIGINT);
    waitpid(ssbd->pid, &status, 0);
    ST_STOP;
  /*  dbg("done\n");*/
    ssbd->pid=0;
}

/* ------- playing -------------------------------------------------- */

int ssbd_play_file(struct ssbd *ssbd, gchar *pfilename){
    SF_INFO sfinfo;
    int subformat;
    
    /*dbg("ssbd_play_file('%s')\n", pfilename);*/
    ssbd_abort(ssbd,1); /*aborts playing or recording */

    memset (&sfinfo, 0, sizeof (sfinfo));

    if (!pfilename || strlen(pfilename)==0) {
        log_adds("No file specified");
        return -1;
    }
    
 /*   dbg ("Playing %s\n", filename);*/
    if (! (ssbd->sndfile = sf_open (pfilename, SFM_READ, &sfinfo))){   
        log_addf("Can't play %s - %s", pfilename, sf_strerror (NULL));
        return -1;
    };

    if (sfinfo.channels < 1 || sfinfo.channels > 2){   
        log_addf ("Channels = %d.", sfinfo.channels);
        return -1;
    };
    
    subformat = sfinfo.format & SF_FORMAT_SUBMASK;
    
    if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE){   
        log_addf("Float point files are not supported");
        return -1;
    }

    /*dbg("sfinfo: format=0x%x channels=%d speed=%d   frames=%d\n", sfinfo.format, sfinfo.channels, sfinfo.samplerate, sfinfo.frames);*/
    dsp->set_format(dsp, &sfinfo);
    if (dsp->open(dsp, 0)<0) {
        log_addf("Can't open DSP %s for playing", dsp->name);
        return -1;
    };
/*    dbg("play opened: format=%x channels=%d speed=%d\n", dsp->format, dsp->channels, dsp->speed);*/

    
    ssbd_thread_create(ssbd, ssbd_play_thread_func);
      
    if (gses) gses->icon=ssbd->playicon;
    return 0;
}

void play_thread_sigint(int a){
    dbg("play_thread_sigint\n");
    ssbd->proc_break=1;
}

gpointer ssbd_play_thread_func(gpointer data){
    gchar *c;
    char s[256];
    int written, readcount;
      char errbuf[1024];
    int err, towrite;
    char *wrptr;
    
    /*dbg("play thread started\n");
    for (i=0;i<6;i++){
        if (ssbd->thread_break)  goto x; 
        dbg("kdák\n");
        sleep(1);
    }
    */
    signal(SIGINT, SIG_DFL);
    while(1){
        if (ssbd->proc_break) {
            dsp->reset(dsp);
            goto x; 
        }
        readcount = sf_read_short (ssbd->sndfile, ssbd->buffer, SSBBUFFER_LEN);
        if (readcount<=0) break;            

        /* todo playmax & etc */

        if (ssbd->proc_break)  {
            dsp->reset(dsp);
            goto x; 
        }

        wrptr=(char*)ssbd->buffer;
        towrite=readcount * sizeof (short);
        while(1){
            written  = dsp->write (dsp, wrptr, towrite);
/*            dbg("written=%d  readcount*%d=%d  errno=%d %s\n",
                        written, sizeof(short), readcount*sizeof(short), errno,strerror_r(err, errbuf, sizeof(errbuf)) );*/
            if (written < 0){
                err=errno;
                LOCK(ssbd);
                c=g_strdup_printf("SSBP;!%s %s %s\n", "Can't write to", dsp->name, strerror_r(err, errbuf, sizeof(errbuf)) );
                UNLOCK(ssbd);
                write(tpipe->threadpipe_write, c, strlen(c));
                g_free(c);
                goto x;
            }
            if (written < readcount * sizeof(short)){
                dbg("write(%s) interrupted after %d bytes\n", dsp->name, written);
                wrptr+=written;
                towrite-=written;
                    
            }
            break;
        }
    }
    dsp->sync(dsp);
    sprintf(s, "SSBP;e\n");
    write(tpipe->threadpipe_write, s, strlen(s));
x:;    
    /*dbg("play thread exited\n");*/
  return NULL;
}

void ssbd_play_read_handler(struct ssbd *ssbd, gchar *str){

    /*dbg("ssbd_read_handler\n");*/
    
    if (gses->last_cq_timer_id){ /* CQ was aborted while playing */
        kill_timer(gses->last_cq_timer_id);
        gses->last_cq_timer_id = 0;
    }

    switch(str[0]){
        case '!':  /* error */
            cq_abort(ssbd->recording); /*abort recording only if it is in progress */
            
            log_addf("ssbd: %s", str+1);
            gses->icon=NULL;
            redraw_later();
            cq_abort(ssbd->recording);
            peer_tx(aband, 0);
            break;
        case 'e':  /* sample played */
          if (!gses) break;
            gses->icon=NULL;
            if (!gses->last_cq) { /* last sample played */
                cq_abort(ssbd->recording);
                break; 
            }
            if (gses->last_cq->ssb_repeat)
                cq_ssb_wait(gses->last_cq);
            else{
                gses->last_cq->type=MOD_NONE;
                cq_abort(ssbd->recording);
            }
            peer_tx(aband, 0);
            redraw_later();
            break;
    }
}

/* ------- recording -------------------------------------------------- */

int ssbd_rec_file(struct ssbd *ssbd){
    int subformat;
    SF_INFO sfinfo;
      char errbuf[1024];
    double df;
    
    /*dbg("ssbd_rec_file  cfg->ssbd_record=%d  ssbd->recording=%d  ctest->recording=%d\n", cfg->ssbd_record, ssbd->recording, ctest?ctest->recording:-123); */

    if (!cfg->ssbd_record) {
        strcpy(errbuf, "recording disabled");
        goto norec;
    }
    if (ssbd->recording) return 0;
    
    gses->icon=ssbd->norecicon;
    if (ctest && !ctest->recording) {
        strcpy(errbuf, "contest too old");
        goto norec;
    }
    

    ssbd_abort(ssbd,1); /*aborts playing or recording */
    ssbd_watchdog(ssbd);

    CONDGFREE(ssbd->rfilename);
    ssbd->serno++;
    ssbd->rfilename=convert_esc(cfg->ssbd_template, NULL, CE_NONE);
    
    /*dbg ("Playing %s\n", ssbd->rfilename);*/
    
    memset (&sfinfo, 0, sizeof (sfinfo));

    sfinfo.format     = cfg->ssbd_format;
    sfinfo.channels   = cfg->ssbd_channels;
    sfinfo.samplerate = cfg->ssbd_samplerate;
    
    if (!sfinfo.format)     sfinfo.format=SF_FORMAT_WAV | SF_FORMAT_PCM_16;
    if (!sfinfo.channels)   sfinfo.channels=1;
    if (!sfinfo.samplerate) sfinfo.samplerate=22050;
    

    fmkdir_p(ssbd->rfilename,0775);

    if ((ssbd->sndfd=open(ssbd->rfilename, O_WRONLY|O_CREAT|O_TRUNC, 0666))<0){
        log_addf ("Error writing file (1) %s: %s",ssbd->rfilename, strerror_r(errno, errbuf, sizeof(errbuf)));
        return -1;
    };

    df=fdf(ssbd->sndfd);
    if (df>=0.0 && cfg->ssbd_diskfree>0){
        if (df < (cfg->ssbd_diskfree*1058576.0)){
            dbg("Not enough free disk space for %s: %d<%d (MiB)\n",ssbd->rfilename, (int)(df/1048576), cfg->ssbd_diskfree);
            log_addf ("Not enough free disk space for %s: %d<%d (MiB)",ssbd->rfilename, (int)(df/1048576), cfg->ssbd_diskfree);
            return -1;
        }
    }

    /*dbg("check:%d\n", sf_format_check(&sfinfo));*/
    
    if (! (ssbd->sndfile = sf_open_fd (ssbd->sndfd, SFM_WRITE, &sfinfo, 1))){   
        log_addf ("Error writing file (2) %s: %s",ssbd->rfilename, sf_strerror (NULL));
        close(ssbd->sndfd);
        return -1;
    };
    
    subformat = sfinfo.format & SF_FORMAT_SUBMASK;

    if (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE){   
        log_addf("Float point files are not supported");
        sf_close (ssbd->sndfile);
        return -1;
    }

    /*dbg("sfinfo: format=%x channels=%d speed=%d   frames=%d\n", sfinfo.format, sfinfo.channels, sfinfo.samplerate, sfinfo.frames);*/
    dsp->set_format(dsp, &sfinfo);
    dsp->set_source(dsp);
/*    dbg("converted format=%x\n", dsp->format);*/
    if (dsp->open(dsp, 1)<0){
        log_addf("Can't open DSP %s for recording", dsp->name);
        sf_close (ssbd->sndfile);
        return -1;
    };
    /*dbg("rec opened: format=%x channels=%d speed=%d\n", dsp->format, dsp->channels, dsp->speed);*/


    
    ssbd_thread_create(ssbd, ssbd_rec_thread_func);
    gses->icon=ssbd->recicon;
    
    ssbd->recording=1;
      return 0;

norec:;    
    if (ssbd->norecshowed) return 0;
    log_addf("Not recording - %s", errbuf); /*TODO stringy do intl*/
    ssbd->norecshowed=1;
    return 0;  
}

void rec_thread_sigint(int a){
    /*dbg("rec_thread_sigint\n");*/
    ssbd->proc_break=1;
}

gpointer ssbd_rec_thread_func(gpointer data){
    gchar *c;
    int readed, writecount=0, err, i, j, max, loglevel;
    unsigned long sum;
    time_t now;
    double df;
    
    
    signal(SIGINT, SIG_DFL);
    while(1){
        if (ssbd->proc_break) break;
        readed  = dsp->read(dsp, ssbd->buffer, SSBBUFFER_LEN);
        if (readed<=0) {
            /*dbg("ssbd_rec_func read<=0\n"); */
            char errstr[1030];
            err=errno;
            LOCK(ssbd);
            c=g_strdup_printf("SSBR;!%s %s %s %d %d\n", "Can't read from", cfg->ssbd_dsp, strerror_r(err, errstr, sizeof(errstr)), readed, err);
            UNLOCK(ssbd);
            write(tpipe->threadpipe_write, c, strlen(c));
            g_free(c);
            break;
        }
      /*  dbg("readed=%d  readed/%d=%d  errno=%d\n",
                    readed, sizeof(short), readed/sizeof(short), errno);*/
        
        if (ssbd->proc_break) break;
        
        if (ssbd->cntlevel==0){
            ssbd->cntlevel=5;
            sum=0;
            max=0;
            for (j=0;j<dsp->channels;j++){
                for (i=0;i<readed/(sizeof(short)*dsp->channels);i+=dsp->channels){
                    short sample=ssbd->buffer[i*dsp->channels+j];
                    if (sample<0) sample=-sample;
                    sum+=sample;
                    if (sample>max) max=sample;
                }
            }
            ssbd->midlevel=sum/(readed/sizeof(short));
            ssbd->maxlevel=max;
            if (max<32) loglevel=0;
            else loglevel=(log(max)-3.465735903)*14;
            if (loglevel>95) loglevel=95;
            c=g_strdup_printf("SSBR;L%d\n", loglevel);
            write(tpipe->threadpipe_write, c, strlen(c));
            g_free(c);
        }else{
            ssbd->cntlevel--;
        }
    
        df=fdf(ssbd->sndfd);
/*        dbg("df=%f df=%d diskfree=%d\n", df, (int)(df/1048576), cfg->ssbd_diskfree);*/
        if (df>=0.0 && cfg->ssbd_diskfree>0){
            if (df < (cfg->ssbd_diskfree*1048576.0)){
                LOCK(ssbd);
                dbg("Not enough free disk space for %s: %d<%d (MiB)\n",ssbd->rfilename, (int)(df/1048576), cfg->ssbd_diskfree);
                c=g_strdup_printf("SSBR;!%s %s %f<%d (MiB)\n", "Not enough free disk space for", ssbd->rfilename, df/1048576.0, cfg->ssbd_diskfree);
                UNLOCK(ssbd);
                write(tpipe->threadpipe_write, c, strlen(c));
                g_free(c);
                break;
            }
        }
        
        writecount = sf_write_short (ssbd->sndfile, ssbd->buffer, readed/sizeof(short));
        if (writecount <=0) {
            char errstr[1030];
            dbg("ssbd_rec_func write<=0\n"); 
            LOCK(ssbd);
            c=g_strdup_printf("SSBR;!%s %s %s\n", "Can't write to", ssbd->rfilename, strerror_r(errno, errstr, sizeof(errstr)));
            UNLOCK(ssbd);
            write(tpipe->threadpipe_write, c, strlen(c));
            g_free(c);
            break;
        }

        now=time(NULL);
        LOCK(ssbd);
        if (ssbd->recstop && now > ssbd->recstop){
            c=g_strdup("SSBR;Q\n");
            write(tpipe->threadpipe_write, c, strlen(c));
            g_free(c);
            UNLOCK(ssbd);
            break;
        }
        UNLOCK(ssbd);
    }
    
    /*dbg("rec_thread exiting\n");*/
      return 0;
}

void ssbd_rec_read_handler(struct ssbd *ssbd, gchar *str){
    /*dbg("ssbd_read_handler\n");*/
    
    switch(str[0]){
        case '!':  /* error */
            cq_abort(1); 
            log_addf("ssbd: %s", str+1);
            if (gses) gses->icon=NULL;
            redraw_later();
            break;
        case 'L':  /* recording level */
            ssbd->loglevel=atoi(str+1);
            /*dbg("level=%d\n", ssbd->loglevel);*/
            break;
        case 'Q':
            ssbd_thread_join(ssbd);
            ssbd_abort(ssbd, 1);
            break;
    }
}


int ssbd_recording(struct ssbd *ssbd){
    return ssbd->recording;
}


/* ------- misc -------------------------------------------------- */

int ssbd_callsign(struct ssbd *ssbd, char *call){
    
    if (!ssbd) return 0;

    LOCK(ssbd);
    CONDGFREE(ssbd->callsign);
    ssbd->callsign=g_strdup(call);
    UNLOCK(ssbd);
      return 0;
}

int mkdir_p(const char *s,mode_t mode) {
    struct stat st;
    gchar *dir,*c;

    /*dbg("mkdir_p '%s'\n", s);*/

    if (!stat(s,&st))
        if (S_ISDIR(st.st_mode)) return(0);

    dir = g_strdup(s);
    c=strrchr(dir,'/');
    if (c==NULL) {
        g_free(dir);
        return(-1);
    }
    *c='\0';
    if (strlen(dir)==0) {
        g_free(dir);
        return(-1);
    }
    mkdir_p(dir,mode);
    g_free(dir);
    return(mkdir(s,mode));
}

int fmkdir_p(const char *filename, mode_t mode){
    gchar *dir,*d;
    int ret;
    
    /*dbg("fmkdir_p '%s'\n", filename);*/
    dir=g_strdup(filename);
    d=strrchr(dir,'/');
    ret=-1;
    if (d){
        *d='\0';
        ret=mkdir_p(dir,mode);
    }
    g_free(dir);
    return ret;
}

gchar *unique_filename(gchar *filename){
    struct stat st;
    int ser;
    gchar *file, *ext, *c;
    char *c1, *c2, *c3;

    c1=c2=c3=NULL;
    if (stat(filename, &st)) return filename; /* filename doesn't exist */
    
    if (regmatch(filename, "(.*)(\\..*)", &c1, &c2, &c3, NULL)==0){
        file=g_strdup(c2);
        ext=g_strdup(c3);
        g_free(filename);
    }else{
        file=filename;
        ext=g_strdup("");
    }
    if (c1) mem_free(c1);
    if (c2) mem_free(c2);
    if (c3) mem_free(c3);
    
    c=NULL;
    for (ser=1; ;ser++){
        /*dbg("file='%s'  ext='%s'\n", file, ext);*/
        c=g_strdup_printf("%s%d%s", file, ser, ext);
/*        dbg("c='%s'\n", c);*/
        if (stat(c, &st)){ /* c doesn't exist */
            break;
        }
        g_free(c);
    }
    return c;
}

void ssbd_watchdog(struct ssbd *ssbd){
    time_t t;

    if (!cfg->ssbd_maxmin) {
        ssbd->recstop=0;
        return;
    }
    t=time(NULL);
    t+=60*cfg->ssbd_maxmin;
    ssbd->recstop=t;
}

void ssbd_play_last_sample(struct ssbd *ssbd){
    if (ssbd->recording){
        cq_abort(1);   /* abort recording */

    }else{
        rx();
    }
    if (ssbd->rfilename && *ssbd->rfilename){
        dbg("rfilename=%s gses->last_cq=%p\n", ssbd->rfilename, gses->last_cq);
        ssbd_play_file(ssbd, ssbd->rfilename);
        gses->icon=ssbd->playicon;
    }else{
        log_adds("No sample recorded");
    }

    redraw_later();
}

Generated by  Doxygen 1.6.0   Back to index