Chinaunix首页 | 论坛 | 博客
  • 博客访问: 831860
  • 博文数量: 125
  • 博客积分: 4066
  • 博客等级: 上校
  • 技术积分: 1401
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-03 18:58
文章分类

全部博文(125)

文章存档

2014年(1)

2013年(1)

2012年(2)

2011年(29)

2010年(92)

我的朋友

分类: LINUX

2010-06-01 19:18:21


static void print_vu_meter_stereo(int *perc, int *maxperc)
{
    const int bar_length = 35;
    char line[80];
    int c;

    memset(line, ' ', sizeof(line) - 1);
    line[bar_length + 3] = '|';

    for (c = 0; c < 2; c++) {
        int p = perc[c] * bar_length / 100;
        char tmp[4];
        if (p > bar_length)
            p = bar_length;
        if (c)
            memset(line + bar_length + 6 + 1, '#', p);
        else
            memset(line + bar_length - p - 1, '#', p);
        p = maxperc[c] * bar_length / 100;
        if (p > bar_length)
            p = bar_length;
        if (c)
            line[bar_length + 6 + 1 + p] = '+';
        else
            line[bar_length - p - 1] = '+';
        if (maxperc[c] > 99)
            sprintf(tmp, "MAX");
        else
            sprintf(tmp, "%02d%%", maxperc[c]);
        if (c)
            memcpy(line + bar_length + 3 + 1, tmp, 3);
        else
            memcpy(line + bar_length, tmp, 3);
    }
    line[bar_length * 2 + 6 + 2] = 0;
    fputs(line, stdout);
}

static void print_vu_meter(signed int *perc, signed int *maxperc)
{
    if (vumeter == VUMETER_STEREO)
        print_vu_meter_stereo(perc, maxperc);
    else
        print_vu_meter_mono(*perc, *maxperc);
}

/* peak handler */
static void compute_max_peak(u_char *data, size_t count)
{
    signed int val, max, perc[2], max_peak[2];
    static    int    run = 0;
    size_t ocount = count;
    int    format_little_endian = snd_pcm_format_little_endian(hwparams.format);    
    int ichans, c;

    if (vumeter == VUMETER_STEREO)
        ichans = 2;
    else
        ichans = 1;

    memset(max_peak, 0, sizeof(max_peak));
    switch (bits_per_sample) {
    case 8: {
        signed char *valp = (signed char *)data;
        signed char mask = snd_pcm_format_silence(hwparams.format);
        c = 0;
        while (count-- > 0) {
            val = *valp++ ^ mask;
            val = abs(val);
            if (max_peak[c] < val)
                max_peak[c] = val;
            if (vumeter == VUMETER_STEREO)
                c = !c;
        }
        break;
    }
    case 16: {
        signed short *valp = (signed short *)data;
        signed short mask = snd_pcm_format_silence_16(hwparams.format);
        signed short sval;

        count /= 2;
        c = 0;
        while (count-- > 0) {
            if (format_little_endian)
                sval = __le16_to_cpu(*valp);
            else
                sval = __be16_to_cpu(*valp);
            sval = abs(sval) ^ mask;
            if (max_peak[c] < sval)
                max_peak[c] = sval;
            valp++;
            if (vumeter == VUMETER_STEREO)
                c = !c;
        }
        break;
    }
    case 24: {
        unsigned char *valp = data;
        signed int mask = snd_pcm_format_silence_32(hwparams.format);

        count /= 3;
        c = 0;
        while (count-- > 0) {
            if (format_little_endian) {
                val = valp[0] | (valp[1]<<8) | (valp[2]<<16);
            } else {
                val = (valp[0]<<16) | (valp[1]<<8) | valp[2];
            }
            /* Correct signed bit in 32-bit value */
            if (val & (1<<(bits_per_sample-1))) {
                val |= 0xff<<24;    /* Negate upper bits too */
            }
            val = abs(val) ^ mask;
            if (max_peak[c] < val)
                max_peak[c] = val;
            valp += 3;
            if (vumeter == VUMETER_STEREO)
                c = !c;
        }
        break;
    }
    case 32: {
        signed int *valp = (signed int *)data;
        signed int mask = snd_pcm_format_silence_32(hwparams.format);

        count /= 4;
        c = 0;
        while (count-- > 0) {
            if (format_little_endian)
                val = __le32_to_cpu(*valp);
            else
                val = __be32_to_cpu(*valp);
            val = abs(val) ^ mask;
            if (max_peak[c] < val)
                max_peak[c] = val;
            valp++;
            if (vumeter == VUMETER_STEREO)
                c = !c;
        }
        break;
    }
    default:
        if (run == 0) {
            fprintf(stderr, _("Unsupported bit size %d.\n"), (int)bits_per_sample);
            run = 1;
        }
        return;
    }
    max = 1 << (bits_per_sample-1);
    if (max <= 0)
        max = 0x7fffffff;

    for (c = 0; c < ichans; c++) {
        if (bits_per_sample > 16)
            perc[c] = max_peak[c] / (max / 100);
        else
            perc[c] = max_peak[c] * 100 / max;
    }

    if (interleaved && verbose <= 2) {
        static int maxperc[2];
        static time_t t=0;
        const time_t tt=time(NULL);
        if(tt>t) {
            t=tt;
            maxperc[0] = 0;
            maxperc[1] = 0;
        }
        for (c = 0; c < ichans; c++)
            if (perc[c] > maxperc[c])
                maxperc[c] = perc[c];

        putchar('\r');
        print_vu_meter(perc, maxperc);
        fflush(stdout);
    }
    else if(verbose==3) {
        printf(_("Max peak (%li samples): 0x%08x "), (long)ocount, max_peak[0]);
        for (val = 0; val < 20; val++)
            if (val <= perc[0] / 5)
                putchar('#');
            else
                putchar(' ');
        printf(" %i%%\n", perc[0]);
        fflush(stdout);
    }
}

static void do_test_position(void)
{
    static long counter = 0;
    static time_t tmr = -1;
    time_t now;
    static float availsum, delaysum, samples;
    static snd_pcm_sframes_t maxavail, maxdelay;
    static snd_pcm_sframes_t minavail, mindelay;
    static snd_pcm_sframes_t badavail = 0, baddelay = 0;
    snd_pcm_sframes_t outofrange;
    snd_pcm_sframes_t avail, delay;
    int err;

    err = snd_pcm_avail_delay(handle, &avail, &delay);
    if (err < 0)
        return;
    outofrange = (test_coef * (snd_pcm_sframes_t)buffer_frames) / 2;
    if (avail > outofrange || avail < -outofrange ||
     delay > outofrange || delay < -outofrange) {
     badavail = avail; baddelay = delay;
     availsum = delaysum = samples = 0;
     maxavail = maxdelay = 0;
     minavail = mindelay = buffer_frames * 16;
     fprintf(stderr, _("Suspicious buffer position (%li total): "
         "avail = %li, delay = %li, buffer = %li\n"),
         ++counter, (long)avail, (long)delay, (long)buffer_frames);
    } else if (verbose) {
        time(&now);
        if (tmr == (time_t) -1) {
            tmr = now;
            availsum = delaysum = samples = 0;
            maxavail = maxdelay = 0;
            minavail = mindelay = buffer_frames * 16;
        }
        if (avail > maxavail)
            maxavail = avail;
        if (delay > maxdelay)
            maxdelay = delay;
        if (avail < minavail)
            minavail = avail;
        if (delay < mindelay)
            mindelay = delay;
        availsum += avail;
        delaysum += delay;
        samples++;
        if (avail != 0 && now != tmr) {
            fprintf(stderr, "BUFPOS: avg%li/%li "
                "min%li/%li max%li/%li (%li) (%li:%li/%li)\n",
                (long)(availsum / samples),
                (long)(delaysum / samples),
                (long)minavail, (long)mindelay,
                (long)maxavail, (long)maxdelay,
                (long)buffer_frames,
                counter, badavail, baddelay);
            tmr = now;
        }
    }
}

/*
 * write function
 */


static ssize_t pcm_write(u_char *data, size_t count)
{
    ssize_t r;
    ssize_t result = 0;

    if (count < chunk_size) {
        snd_pcm_format_set_silence(hwparams.format, data + count * bits_per_frame / 8, (chunk_size - count) * hwparams.channels);
        count = chunk_size;
    }
    while (count > 0) {
        if (test_position)
            do_test_position();
        r = writei_func(handle, data, count);
        if (test_position)
            do_test_position();
        if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
            if (!test_nowait)
                snd_pcm_wait(handle, 1000);
        } else if (r == -EPIPE) {
            xrun();
        } else if (r == -ESTRPIPE) {
            suspend();
        } else if (r < 0) {
            error(_("write error: %s"), snd_strerror(r));
            prg_exit(EXIT_FAILURE);
        }
        if (r > 0) {
            if (vumeter)
                compute_max_peak(data, r * hwparams.channels);
            result += r;
            count -= r;
            data += r * bits_per_frame / 8;
        }
    }
    return result;
}


/*
 * read function
 */


static ssize_t pcm_read(u_char *data, size_t rcount)
{
    ssize_t r;
    size_t result = 0;
    size_t count = rcount;

    if (count != chunk_size) {
        count = chunk_size;
    }

    while (count > 0) {
        if (test_position)
            do_test_position();
        r = readi_func(handle, data, count);
        if (test_position)
            do_test_position();
        if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
            if (!test_nowait)
                snd_pcm_wait(handle, 1000);
        } else if (r == -EPIPE) {
            xrun();
        } else if (r == -ESTRPIPE) {
            suspend();
        } else if (r < 0) {
            error(_("read error: %s"), snd_strerror(r));
            prg_exit(EXIT_FAILURE);
        }
        if (r > 0) {
            if (vumeter)
                compute_max_peak(data, r * hwparams.channels);
            result += r;
            count -= r;
            data += r * bits_per_frame / 8;
        }
    }
    return rcount;
}


/* calculate the data count to read from/to dsp */
static off64_t calc_count(void)
{
    off64_t count;

    if (timelimit == 0) {
        count = pbrec_count;
    } else {
        count = snd_pcm_format_size(hwparams.format, hwparams.rate * hwparams.channels);
        count *= (off64_t)timelimit;
    }
    return count < pbrec_count ? count : pbrec_count;
}


/* write a WAVE-header */
static void begin_wave(int fd, size_t cnt)
{
    WaveHeader h;
    WaveFmtBody f;
    WaveChunkHeader cf, cd;
    int bits;
    u_int tmp;
    u_short tmp2;

    /* WAVE cannot handle greater than 32bit (signed?) int */
    if (cnt == (size_t)-2)
        cnt = 0x7fffff00;

    bits = 8;
    switch ((unsigned long) hwparams.format) {
    case SND_PCM_FORMAT_U8:
        bits = 8;
        break;
    case SND_PCM_FORMAT_S16_LE:
        bits = 16;
        break;
    case SND_PCM_FORMAT_S32_LE:
        case SND_PCM_FORMAT_FLOAT_LE:
        bits = 32;
        break;
    case SND_PCM_FORMAT_S24_LE:
    case SND_PCM_FORMAT_S24_3LE:
        bits = 24;
        break;
    default:
        error(_("Wave doesn't support %s format..."), snd_pcm_format_name(hwparams.format));
        prg_exit(EXIT_FAILURE);
    }
    h.magic = WAV_RIFF;
    tmp = cnt + sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + sizeof(WaveChunkHeader) - 8;
    h.length = LE_INT(tmp);
    h.type = WAV_WAVE;

    cf.type = WAV_FMT;
    cf.length = LE_INT(16);

        if (hwparams.format == SND_PCM_FORMAT_FLOAT_LE)
                f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT);
        else
                f.format = LE_SHORT(WAV_FMT_PCM);
    f.channels = LE_SHORT(hwparams.channels);
    f.sample_fq = LE_INT(hwparams.rate);
#if 0
    tmp2 = (samplesize == 8) ? 1 : 2;
    f.byte_p_spl = LE_SHORT(tmp2);
    tmp = dsp_speed * hwparams.channels * (u_int) tmp2;
#else
    tmp2 = hwparams.channels * snd_pcm_format_physical_width(hwparams.format) / 8;
    f.byte_p_spl = LE_SHORT(tmp2);
    tmp = (u_int) tmp2 * hwparams.rate;
#endif
    f.byte_p_sec = LE_INT(tmp);
    f.bit_p_spl = LE_SHORT(bits);

    cd.type = WAV_DATA;
    cd.length = LE_INT(cnt);

    if (write(fd, &h, sizeof(WaveHeader)) != sizeof(WaveHeader) ||
     write(fd, &cf, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader) ||
     write(fd, &f, sizeof(WaveFmtBody)) != sizeof(WaveFmtBody) ||
     write(fd, &cd, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader)) {
        error(_("write error"));
        prg_exit(EXIT_FAILURE);
    }
}


static void end_wave(int fd)
{                /* only close output */
    WaveChunkHeader cd;
    off64_t length_seek;
    off64_t filelen;
    u_int rifflen;
    
    length_seek = sizeof(WaveHeader) +
         sizeof(WaveChunkHeader) +
         sizeof(WaveFmtBody);
    cd.type = WAV_DATA;
    cd.length = fdcount > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(fdcount);
    filelen = fdcount + 2*sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + 4;
    rifflen = filelen > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(filelen);
    if (lseek64(fd, 4, SEEK_SET) == 4)
        write(fd, &rifflen, 4);
    if (lseek64(fd, length_seek, SEEK_SET) == length_seek)
        write(fd, &cd, sizeof(WaveChunkHeader));
    if (fd != 1)
        close(fd);
}


static void header(int rtype, char *name)
{
    if (!quiet_mode) {
        if (! name)
            name = (stream == SND_PCM_STREAM_PLAYBACK) ? "stdout" : "stdin";
        fprintf(stderr, "%s %s '%s' : ",
            (stream == SND_PCM_STREAM_PLAYBACK) ? _("Playing") : _("Recording"),
            gettext(fmt_rec_table[rtype].what),
            name);
        fprintf(stderr, "%s, ", snd_pcm_format_description(hwparams.format));
        fprintf(stderr, _("Rate %d Hz, "), hwparams.rate);
        if (hwparams.channels == 1)
            fprintf(stderr, _("Mono"));
        else if (hwparams.channels == 2)
            fprintf(stderr, _("Stereo"));
        else
            fprintf(stderr, _("Channels %i"), hwparams.channels);
        fprintf(stderr, "\n");
    }
}

/* playing raw data */

static void playback_go(int fd, size_t loaded, off64_t count, int rtype, char *name)
{
    int l, r;
    off64_t written = 0;
    off64_t c;

    header(rtype, name);
    set_params();

    while (loaded > chunk_bytes && written < count) {
        if (pcm_write(audiobuf + written, chunk_size) <= 0)
            return;
        written += chunk_bytes;
        loaded -= chunk_bytes;
    }
    if (written > 0 && loaded > 0)
        memmove(audiobuf, audiobuf + written, loaded);

    l = loaded;
    while (written < count) {
        do {
            c = count - written;
            if (c > chunk_bytes)
                c = chunk_bytes;
            c -= l;

            if (c == 0)
                break;
            r = safe_read(fd, audiobuf + l, c);
            if (r < 0) {
                perror(name);
                prg_exit(EXIT_FAILURE);
            }
            fdcount += r;
            if (r == 0)
                break;
            l += r;
        } while ((size_t)l < chunk_bytes);
        l = l * 8 / bits_per_frame;
        r = pcm_write(audiobuf, l);
        if (r != l)
            break;
        r = r * bits_per_frame / 8;
        written += r;
        l = 0;
    }
    snd_pcm_nonblock(handle, 0);
    snd_pcm_drain(handle);
    snd_pcm_nonblock(handle, nonblock);
}


/*
 * let's play or capture it (capture_type says VOC/WAVE/raw)
 */


static void playback(char *name)
{
    int ofs;
    size_t dta;
    ssize_t dtawave;

    pbrec_count = LLONG_MAX;
    fdcount = 0;
    if (!name || !strcmp(name, "-")) {
        fd = fileno(stdin);
        name = "stdin";
    } else {
        if ((fd = open64(name, O_RDONLY, 0)) == -1) {
            perror(name);
            prg_exit(EXIT_FAILURE);
        }
    }
    /* read the file header */
    dta = sizeof(AuHeader);
    if ((size_t)safe_read(fd, audiobuf, dta) != dta) {
        error(_("read error"));
        prg_exit(EXIT_FAILURE);
    }
    /* read bytes for WAVE-header */
    if ((dtawave = test_wavefile(fd, audiobuf, dta)) >= 0) {
        pbrec_count = calc_count();
        playback_go(fd, dtawave, pbrec_count, FORMAT_WAVE, name);
    }
      __end:
    if (fd != 0)
        close(fd);
}

/**
 * mystrftime
 *
 * Variant of strftime(3) that supports additional format
 * specifiers in the format string.
 *
 * Parameters:
 *
 * s     - destination string
 * max    - max number of bytes to write
 * userformat - format string
 * tm     - time information
 * filenumber - the number of the file, starting at 1
 *
 * Returns: number of bytes written to the string s
 */

size_t mystrftime(char *s, size_t max, const char *userformat,
         const struct tm *tm, const int filenumber)
{
    char formatstring[PATH_MAX] = "";
    char tempstring[PATH_MAX] = "";
    char *format, *tempstr;
    const char *pos_userformat;

    format = formatstring;

    /* if mystrftime is called with userformat = NULL we return a zero length string */
    if (userformat == NULL) {
        *s = '\0';
        return 0;
    }

    for (pos_userformat = userformat; *pos_userformat; ++pos_userformat) {
        if (*pos_userformat == '%') {
            tempstr = tempstring;
            tempstr[0] = '\0';
            switch (*++pos_userformat) {

                case '\0': // end of string

                    --pos_userformat;
                    break;

                case 'v': // file number

                    sprintf(tempstr, "%02d", filenumber);
                    break;

                default: // All other codes will be handled by strftime

                    *format++ = '%';
                    *format++ = *pos_userformat;
                    continue;
            }

            /* If a format specifier was found and used, copy the result. */
            if (tempstr[0]) {
                while ((*format = *tempstr++) != '\0')
                    ++format;
                continue;
            }
        }

        /* For any other character than % we simply copy the character */
        *format++ = *pos_userformat;
    }

    *format = '\0';
    format = formatstring;
    return strftime(s, max, format, tm);
}

static int new_capture_file(char *name, char *namebuf, size_t namelen,
             int filecount)
{
    char *s;
    char buf[PATH_MAX+1];
    time_t t;
    struct tm *tmp;

    if (use_strftime) {
        t = time(NULL);
        tmp = localtime(&t);
        if (tmp == NULL) {
            perror("localtime");
            prg_exit(EXIT_FAILURE);
        }
        if (mystrftime(namebuf, namelen, name, tmp, filecount+1) == 0) {
            fprintf(stderr, "mystrftime returned 0");
            prg_exit(EXIT_FAILURE);
        }
        return filecount;
    }

    /* get a copy of the original filename */
    strncpy(buf, name, sizeof(buf));

    /* separate extension from filename */
    s = buf + strlen(buf);
    while (s > buf && *s != '.' && *s != '/')
        --s;
    if (*s == '.')
        *s++ = 0;
    else if (*s == '/')
        s = buf + strlen(buf);

    /* upon first jump to this if block rename the first file */
    if (filecount == 1) {
        if (*s)
            snprintf(namebuf, namelen, "%s-01.%s", buf, s);
        else
            snprintf(namebuf, namelen, "%s-01", buf);
        remove(namebuf);
        rename(name, namebuf);
        filecount = 2;
    }

    /* name of the current file */
    if (*s)
        snprintf(namebuf, namelen, "%s-%02i.%s", buf, filecount, s);
    else
        snprintf(namebuf, namelen, "%s-%02i", buf, filecount);

    return filecount;
}

/**
 * create_path
 *
 * This function creates a file path, like mkdir -p.
 *
 * Parameters:
 *
 * path - the path to create
 *
 * Returns: 0 on success, -1 on failure
 * On failure, a message has been printed to stderr.
 */

int create_path(const char *path)
{
    char *start;
    mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;

    if (path[0] == '/')
        start = strchr(path + 1, '/');
    else
        start = strchr(path, '/');

    while (start) {
        char *buffer = strdup(path);
        buffer[start-path] = 0x00;

        if (mkdir(buffer, mode) == -1 && errno != EEXIST) {
            fprintf(stderr, "Problem creating directory %s", buffer);
            perror(" ");
            free(buffer);
            return -1;
        }
        free(buffer);
        start = strchr(start + 1, '/');
    }
    return 0;
}

static int safe_open(const char *name)
{
    int fd;

    fd = open64(name, O_WRONLY | O_CREAT, 0644);
    if (fd == -1) {
        if (errno != ENOENT || !use_strftime)
            return -1;
        if (create_path(name) == 0)
            fd = open64(name, O_WRONLY | O_CREAT, 0644);
    }
    return fd;
}

static void capture(char *orig_name)
{
    int tostdout=0;        /* boolean which describes output stream */
    int filecount=0;    /* number of files written */
    char *name = orig_name;    /* current filename */
    char namebuf[PATH_MAX+1];
    off64_t count, rest;        /* number of bytes to capture */

    /* get number of bytes to capture */
    count = calc_count();
    if (count == 0)
        count = LLONG_MAX;
    /* compute the number of bytes per file */
    max_file_size = max_file_time *
        snd_pcm_format_size(hwparams.format,
                 hwparams.rate * hwparams.channels);
    /* WAVE-file should be even (I'm not sure), but wasting one byte
     isn't a problem (this can only be in 8 bit mono) */

    if (count < LLONG_MAX)
        count += count % 2;
    else
        count -= count % 2;

    /* display verbose output to console */
    header(file_type, name);

    /* setup sound hardware */
    set_params();

    /* write to stdout? */
    if (!name || !strcmp(name, "-")) {
        fd = fileno(stdout);
        name = "stdout";
        tostdout=1;
        if (count > fmt_rec_table[file_type].max_filesize)
            count = fmt_rec_table[file_type].max_filesize;
    }

    do {
        /* open a file to write */
        if(!tostdout) {
            /* upon the second file we start the numbering scheme */
            if (filecount || use_strftime) {
                filecount = new_capture_file(orig_name, namebuf,
                             sizeof(namebuf),
                             filecount);
                name = namebuf;
            }
            
            /* open a new file */
            remove(name);
            fd = safe_open(name);
            if (fd < 0) {
                perror(name);
                prg_exit(EXIT_FAILURE);
            }
            filecount++;
        }

        rest = count;
        if (rest > fmt_rec_table[file_type].max_filesize)
            rest = fmt_rec_table[file_type].max_filesize;
        if (max_file_size && (rest > max_file_size))
            rest = max_file_size;

        /* setup sample header */
        if (fmt_rec_table[file_type].start)
            fmt_rec_table[file_type].start(fd, rest);

        /* capture */
        fdcount = 0;
        while (rest > 0 && recycle_capture_file == 0) {
            size_t c = (rest <= (off64_t)chunk_bytes) ?
                (size_t)rest : chunk_bytes;
            size_t f = c * 8 / bits_per_frame;
            if (pcm_read(audiobuf, f) != f)
                break;
            if (write(fd, audiobuf, c) != c) {
                perror(name);
                prg_exit(EXIT_FAILURE);
            }
            count -= c;
            rest -= c;
            fdcount += c;
        }

        /* re-enable SIGUSR1 signal */
        if (recycle_capture_file) {
            recycle_capture_file = 0;
            signal(SIGUSR1, signal_handler_recycle);
        }

        /* finish sample container */
        if (fmt_rec_table[file_type].end && !tostdout) {
            fmt_rec_table[file_type].end(fd);
            fd = -1;
        }

        /* repeat the loop when format is raw without timelimit or
         * requested counts of data are recorded
         */

    } while ( count > 0);
}


阅读(1961) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~