summaryrefslogtreecommitdiff
path: root/oss.rs
blob: edfad37e22987fdc6b1ad3805bff4e5648c64f8f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use std::libc::{c_int, c_void, close, O_RDONLY, open, read, size_t};
use std::rt::io::{io_error, IoError, OtherIoError};
use std::vec;

macro_rules! _SIO(
  ($x: expr, $y: expr) => (($x << 8) | $y);
)

/* rust doesn't have a sizeof you can use in constant expressions >.<
macro_rules! _SIOWR(
  ($x: expr, $y: expr, $t: ty) => (
    0xc0000000 as c_int | (size_of::<$t>() as c_int << 16) | ($x << 8) | $y);
)
*/

macro_rules! _SIOWR(
  ($x: expr, $y: expr, $t: expr) => (
    0xc0000000 as c_int | ($t << 16) | ($x << 8) | $y);
)

static SNDCTL_DSP_RESET: c_int = _SIO!('P' as c_int, 0);
static SNDCTL_DSP_SYNC: c_int = _SIO!('P' as c_int, 1);
static SNDCTL_DSP_SPEED: c_int = _SIOWR!('P' as c_int, 2, 4);
static SNDCTL_DSP_STEREO: c_int = _SIOWR!('P' as c_int, 3, 4);
static SNDCTL_DSP_GETBLKSIZE: c_int = _SIOWR!('P' as c_int, 4, 4);
static SNDCTL_DSP_SETFMT: c_int = _SIOWR!('P' as c_int, 5, 4);
static AFMT_S16_LE: c_int = 0x00000010;

extern {
  fn ioctl(fd: c_int, ioctl: c_int, arg: *mut c_int) -> c_int;
}

pub struct OssDevice {
  fd: c_int,
}

impl OssDevice {
  #[fixed_stack_segment]
  pub fn new(path: &Path) -> Option<OssDevice> {
    let fd = do path.with_c_str |path| {
      unsafe { open(path, O_RDONLY, 0777) }
    };
    if fd != -1 {
      Some(OssDevice { fd: fd })
    } else {
      None
    }
  }
  #[fixed_stack_segment]
  pub fn reset(&self) {
    unsafe {
      if ioctl(self.fd, SNDCTL_DSP_RESET, 0 as *mut c_int) == -1 {
        io_error::cond.raise(IoError { kind: OtherIoError,
                                       desc: "Unable to reset dsp",
                                       detail: None });
      }
    };
  }
  #[fixed_stack_segment]
  pub fn set_format(&self) {
    unsafe {
      let mut i: c_int = AFMT_S16_LE;
      if ioctl(self.fd, SNDCTL_DSP_SETFMT, &mut i) == -1 {
        io_error::cond.raise(IoError { kind: OtherIoError,
                                       desc: "Unable to set dsp format",
                                       detail: None });
      }
    };
  }
  #[fixed_stack_segment]
  pub fn set_stereo(&self) {
    unsafe {
      let mut i: c_int = 1;
      if ioctl(self.fd, SNDCTL_DSP_STEREO, &mut i) == -1 {
        io_error::cond.raise(IoError { kind: OtherIoError,
                                       desc: "Unable to set dsp stereo",
                                       detail: None });
      }
    };
  }
  #[fixed_stack_segment]
  pub fn set_speed(&self, rate: int) {
    unsafe {
      let mut i: c_int = rate as c_int;
      if ioctl(self.fd, SNDCTL_DSP_SPEED, &mut i) == -1 {
        io_error::cond.raise(IoError { kind: OtherIoError,
                                       desc: "Unable to set dsp speed",
                                       detail: None });
      }
    };
  }
  #[fixed_stack_segment]
  pub fn get_block_size(&self) -> uint {
    unsafe {
      let mut i: c_int = 0;
      if ioctl(self.fd, SNDCTL_DSP_GETBLKSIZE, &mut i) == -1 {
        io_error::cond.raise(IoError { kind: OtherIoError,
                                       desc: "Unable to get dsp block size",
                                       detail: None });
      };
      i as uint
    }
  }
  #[fixed_stack_segment]
  pub fn sync(&self) {
    unsafe {
      if ioctl(self.fd, SNDCTL_DSP_SYNC, 0 as *mut c_int) == -1 {
        io_error::cond.raise(IoError { kind: OtherIoError,
                                       desc: "Unable to get sync dsp",
                                       detail: None });
      };
    }
  }
  #[fixed_stack_segment]
  pub fn read_all(&self, chan: &Chan<~[u8]>) {
    let block_size = self.get_block_size();
    self.sync();
    loop {
      let mut buffer: ~[u8] = vec::with_capacity(block_size);
      unsafe {
        let length = read(self.fd,
                          vec::raw::to_mut_ptr(buffer) as *mut c_void,
                          block_size as size_t);
        if length == -1 {
          break;
        }
        vec::raw::set_len(&mut buffer, length as uint);
      }
      chan.send(buffer);
    }
  }
}

impl Drop for OssDevice {
  #[fixed_stack_segment]
  fn drop(&mut self) {
    unsafe { close(self.fd) };
  }
}