File: sblaster.c

package info (click to toggle)
acfax 981011-17
  • links: PTS
  • area: main
  • in suites: bullseye, buster, jessie, jessie-kfreebsd, sid, stretch
  • size: 672 kB
  • sloc: ansic: 5,502; makefile: 36
file content (299 lines) | stat: -rw-r--r-- 7,479 bytes parent folder | download | duplicates (7)
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/*
    ACfax - Fax reception with X11-interface for amateur radio
    Copyright (C) 1995-1998 Andreas Czechanowski, DL4SDC

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software Foundation,
    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

    andreas.czechanowski@ins.uni-stuttgart.de
*/
    
/*
 * sblaster.c - this file should contain all hardware-dependent functions
 *		on top of mod_demod.c for the SoundBlaster under linux.
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include "mod_demod.h"
#include "sblaster.h"
#include <unistd.h>

static int afd, mfd;
static int blsize;
static int decsize;
static int amode;
static int aspeed;
static int afilter;
static int adevi;
static int amaxval;
int	rx_level;
int	dspfd;
char	*smplptr;
char	*decptr;

/* this constants should go to a configuration file in future.... */
unsigned fm_smplf = (unsigned)(FM_SMPLF * 65536);
unsigned am_smplf = (unsigned)(AM_SMPLF * 65536);

static void set_speed(int);

/* initialize the interface: open it, set it to mono, 8bits per sample,
 * and allocate space for the input- and output-buffers of the modem-
 * routines. return a pointer to the decoded data and the number of
 * values per call to do_receive.
 */
void interface_init(char **dta_return, int *cnt_return)
{
  int speed = 8000;
  int bits = 8;
  int stereo = 0;
  int linevol = 80;
  int mastervol = 55;
#ifdef SBL_16
  int recsrc = SOUND_MASK_LINE;
#endif
  static int inited = 0;

  /* already initialized ? return values although... */
  if (inited) {
    *dta_return = decptr;
    *cnt_return = decsize;
    return;
  }

  fprintf(stderr, "opening and initializing sound-interface\n");
  if ((afd = open(DSPDEV, O_RDWR, 0666)) < 0) {
    perror("open_dsp");
    exit(1);
  }
  dspfd = afd;
#ifdef SBL_16
  if ((mfd = open(MIXDEV, O_RDWR, 0666)) < 0) {
    perror("open_mixer");
    exit(1);
  }
  linevol &= 0xff;
  linevol += linevol << 8;
  mastervol &= 0xff;
  mastervol += mastervol << 8;
  if ((ioctl(mfd, SOUND_MIXER_WRITE_LINE, &linevol) < 0) ||
      (ioctl(mfd, SOUND_MIXER_WRITE_VOLUME, &mastervol) < 0) ||
      (ioctl(mfd, SOUND_MIXER_WRITE_RECSRC, &recsrc) < 0)) {
    perror("mixer_ioctl");
    exit(1);
  }
  close (mfd);
#endif
  if ((ioctl(afd, SNDCTL_DSP_SPEED, &speed) < 0) ||
      (ioctl(afd, SNDCTL_DSP_SAMPLESIZE, &bits) < 0) ||
      (ioctl(afd, SNDCTL_DSP_STEREO, &stereo) < 0) ||
      (ioctl(afd, SNDCTL_DSP_GETBLKSIZE, &blsize) < 0)) {
    perror("dsp_ioctl");
    exit(1);
  }
  decsize = blsize / 2;
  /* set up defaults for the modem */
  amode = MOD_FM;
  afilter = FIL_MIDL;
  adevi = 400;
  set_modem_param (afilter, amaxval, adevi);
  if (!(decptr = malloc((decsize))) ||
      !(smplptr = malloc(blsize))) {
    perror("setup_buffers");
    exit(1);
  }
  printf("block size is %d bytes\n", blsize);
  *dta_return = decptr;
  *cnt_return = decsize;

  inited = -1;
}

/* stop the interface, don't let it overrun by no more reading/writing */
void interface_stop(void)
{
  if (ioctl(afd, SNDCTL_DSP_RESET, NULL) < 0) {
    perror("interface_stop: cannot reset pcm");
  }
}

/* set up the desired mode, the decoded value range and the deviation for
 * FM. Return the number of demodulated sample-points per second (for APT)
 */
void setup_mode(int mode, int maxval, int devi, unsigned *smplf)
{
  char *dummy1;
  int dummy2;
  int id;

  /* be sure interface is initialized */
  interface_init(&dummy1, &dummy2);

  if ((id = (mode & MOD_BITS))) {
    switch (id) {
      case MOD_FM:
	set_speed(8000);
	break;
      case MOD_AM:
	set_speed(9600);
	break;
    }
    amode = id;
    fprintf(stderr, "setting demodulator mode to 0x%04x\n", amode);
  }
  /* range checking for afilter, adevi and amaxval is done in mod_demod.c */
  if ((id = mode & FIL_MASK)) {
    afilter = id;
    fprintf(stderr, "setting filter selection to 0x%04x\n", afilter);
  }
  if (devi) {
    adevi = devi;
    fprintf(stderr, "setting deviation to %d\n", adevi);
  }
  if (maxval) {
    amaxval = maxval;
    fprintf(stderr, "setting max.demod.value to %d\n", amaxval);
  }
  if ((mode & FIL_MASK) | (devi) | (maxval)) {
    set_modem_param (afilter, amaxval, adevi);
  }
  /* return the number of samples per second (16 bits int, 16 bits frac) */
  if (smplf) {
    switch(amode) {
      case MOD_AM:
	*smplf = am_smplf;
	break;
      case MOD_FM:
	*smplf = fm_smplf;
	break;
    }
  }
}

/* receive the signal, demodulate it, ant put the demodulated values
 * into an array starting at decptr with decsize bytes. (1 byte / value)
 */
void do_receive(void)
{
  int cnt;
  int hi, lo;
  int i, sample;

  cnt = read(afd, smplptr, blsize);
  if (cnt <= 0) {
    perror("do_receive");
    exit(1);
  }
  fputc('<', stderr);
  switch(amode) {
    case MOD_AM:
	am_demod(smplptr, cnt, decptr);
	break;
    case MOD_FM:
	fm_demod(smplptr, cnt, decptr);
	break;
    default:
	return;
  }
  /* determine the signal level and put it into rx_level (0..255) */
  lo = 255;
  hi = 0;
  for (i = 0; i < blsize / 4; i++)
  {
    sample = smplptr[i] & 0xff;
    if (sample > hi) hi = sample;
    if (sample < lo) lo = sample;
  }
  rx_level = hi - lo;
}

/* modulate the sample-values from decptr into smplptr, and transmit them
 * over the interface. The number of input-values is always decsize.
 */
void do_transmit(void)
{
  int cnt;

  switch(amode) {
    case MOD_FM:
	fm_modulate(decptr, decsize, smplptr);
    case MOD_AM:
	am_modulate(decptr, decsize, smplptr);
    default:
	return;
  }
  cnt = write(afd, smplptr, blsize);
  if (cnt <= 0) {
    perror("do_transmit");
    exit(1);
  }
  fputc('>', stderr);
}

/* check for signal level, return a value between 0 and 256 */
int signal_level(void)
{
  char buf[512];
  int i, p, lo, hi;
  
  if (read(afd, buf, 512) != 512) {
    perror("signal_level: cannot get samples");
  }
  if (ioctl(afd, SNDCTL_DSP_SYNC, NULL) < 0) {
    perror("signal_level: cannot reset pcm");
  }
  lo = 255;
  hi = 0;
  for (i=0; i<512; i++) {
    p = buf[i]&0xff;
    if (p > hi) hi = p;
    if (p < lo) lo = p;
  }
  return (hi-lo);
}

/* just assign new value to either the AM or FM sample-frequency,
   and hold values permanently until next change
*/
void tune_frequency(unsigned freq)
{
  switch(amode) {
    case MOD_FM:
	fm_smplf = freq;
    case MOD_AM:
	am_smplf = freq;
    default:
	return;
  }
}

/* set a new sample-rate for the DSP-device. We need to restart input
   in order to do so, because Linux does not update the SB-registers
   when sending the command, but only on start of input/output
*/
static void set_speed(int speed)
{
  if (ioctl(afd, SNDCTL_DSP_SPEED, &speed) < 0) {
    perror("set_speed: cannot set new sample-rate");
  }
  fprintf(stderr,"speed set to %d\n", speed);
  if (ioctl(afd, SNDCTL_DSP_SYNC, NULL) < 0) {
    perror("set_speed: cannot reset pcm");
  }
  aspeed = speed;
}