"Fossies" - the Fresh Open Source Software archive

Member "xanalyser-1.32/src/pcm-oss.cc" of archive xanalyser-1.32.tar.gz:




/*
 *  Author: Arvin Schnell
 */


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#ifdef __NetBSD__
#  include <soundcard.h>
#else
#  include <sys/soundcard.h>
#endif
#include <math.h>
#include <errno.h>
#include <string.h>
#include <iostream>

using std::cerr;

#include "pcm-oss.h"


PCMOSS::PCMOSS (const char* name, pcm_type_t type, pcm_mode_t mode,
		pcm_format_t format, int channels, int rate, int buffer_size)
    : PCM (name, type, mode, format, channels, rate, buffer_size),
      fd (-1)
{
    if (channels != 1 && channels != 2) {
	cerr << "error: Illegal number of channels\n";
	return;
    }

    if (format != S8 && format != S16) {
	cerr << "error: Illegal format (resolution)\n";
	return;
    }

    int openmode = mode == WRITE ? O_WRONLY : O_RDONLY;
    fd = open (name, openmode, 0);
    if (fd == -1) {
	cerr << "error: Can't open " << name << ": "
	     << strerror (errno) << '\n';
	return;
    }

    const int want_buffer_size = get_bytes_per_frame () * buffer_size;
    int s = (int) (floor (log (want_buffer_size) / log (2.0) + 0.5));
    if ((1L << s) != want_buffer_size) {
	cerr << "error: Invalid requested buffer size\n";
	return;
    }

    int arg = 0x00020000 + s;
    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &arg)) {
	cerr << "error: Can't set fragment size: "
	     << strerror (errno) << '\n';
	return;
    }

    const int want_format = format == S8 ? AFMT_S8 : AFMT_S16_NE;
    int tmp_format = want_format;
    if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp_format) == -1) {
	cerr << "error: " << strerror (errno) << '\n';
	return;
    }

    if (want_format != tmp_format) {
	cerr << "error: Can't set requested format (resolution)\n";
	return;
    }

    const int want_stereo = channels == 1 ? 0 : 1;
    int tmp_stereo = want_stereo;
    if (ioctl (fd, SNDCTL_DSP_STEREO, &tmp_stereo) == -1) {
	cerr << "error: " << strerror (errno) << '\n';
	return;
    }

    if (want_stereo != tmp_stereo) {
	cerr << "error: Can't set mono/stereo\n";
	return;
    }

    int tmp_rate = rate;
    if (ioctl (fd, SNDCTL_DSP_SPEED, &tmp_rate) == -1) {
	cerr << "error: " << strerror (errno) << '\n';
	return;
    }

    if (abs (rate - tmp_rate) > 1) {	// we can tolerate 1 Hz difference
	cerr << "error: Can't set requested rate\n"
	     << "requested rate: " << rate << " Hz, actual rate: "
	     << tmp_rate << " Hz\n";
	return;
    }

    int tmp_buffer_size = want_buffer_size;
    if (ioctl (fd, SNDCTL_DSP_GETBLKSIZE, &tmp_buffer_size) == -1) {
	cerr << "error " << strerror (errno) << '\n';
	return;
    }

    if (want_buffer_size != tmp_buffer_size) {
	cerr << "error: Can't set requested buffer size\n"
	     << "requested size: " << want_buffer_size << " bytes, actual size: "
	     << tmp_buffer_size << " bytes\n";
	return;
    }

    ok = true;
}


PCMOSS::~PCMOSS ()
{
    if (fd > 0)
	close (fd);
}


void
PCMOSS::info () const
{
    cerr << "driver: oss\n";
    PCM::info ();
}


size_t
PCMOSS::size ()
{
    return (size_t)(-1);
}


off_t
PCMOSS::seek (off_t, int)
{
    return (off_t)(-1);
}


size_t
PCMOSS::read (void* buffer, size_t frames)
{
    int i = get_bytes_per_frame () * frames;

    if (::read (fd, buffer, i) != i) {
	cerr << "error: " << strerror (errno) << '\n';
	return (size_t)(-1);
    }

    return frames;
}


size_t
PCMOSS::write (void* buffer, size_t frames)
{
    int i = get_bytes_per_frame () * frames;

    if (::write (fd, buffer, i) != i) {
	cerr << "error: " << strerror (errno) << '\n';
	return (size_t)(-1);
    }

    return frames;
}