summaryrefslogtreecommitdiff
path: root/tutorials/sndkit/samples/audiolevel.c
blob: 38734e3511bf7a549847097982da4db892c27ee6 (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
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
/*
 * Purpose: A simple program that does audio recording.
 * Copyright (C) 4Front Technologies, 2002-2004. Released under GPLv2/CDDL.
 *
 * Description:
 * This is a very minimal program that does audio recording. However to
 * demonstrate processiong of audio data it computes the
 * maximum value of the signal and displays a "LED" bars
 * (using character mode console).
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <soundcard.h>

int fd_in;
int sample_rate = 48000;

/*
 * The open_audio_device opens the audio device and initializes it 
 * for the required mode. The same routine is used in the other
 * simple sample programs too.
 */

static int
open_audio_device (char *name, int mode)
{
  int tmp, fd;

  if ((fd = open (name, mode, 0)) == -1)
    {
      perror (name);
      exit (-1);
    }

/*
 * Setup the device. Note that it's important to set the
 * sample format, number of channels and sample rate exactly in this order.
 * Some devices depend on the order.
 */

/*
 * Set the sample format
 */
  tmp = AFMT_S16_NE;		/* Native 16 bits */
  if (ioctl (fd, SNDCTL_DSP_SETFMT, &tmp) == -1)
    {
      perror ("SNDCTL_DSP_SETFMT");
      exit (-1);
    }

  if (tmp != AFMT_S16_NE)
    {
      fprintf (stderr,
	       "The device doesn't support the 16 bit sample format.\n");
      exit (-1);
    }

/*
 * Set the number of channels (mono)
 */
  tmp = 1;
  if (ioctl (fd, SNDCTL_DSP_CHANNELS, &tmp) == -1)
    {
      perror ("SNDCTL_DSP_CHANNELS");
      exit (-1);
    }

  if (tmp != 1)
    {
      fprintf (stderr, "The device doesn't support mono mode.\n");
      exit (-1);
    }

/*
 * Set the sample rate
 */
  sample_rate = 48000;
  if (ioctl (fd, SNDCTL_DSP_SPEED, &sample_rate) == -1)
    {
      perror ("SNDCTL_DSP_SPEED");
      exit (-1);
    }

/*
 * No need for error checking because we will automatically adjust the
 * signal based on the actual sample rate. However most application must
 * check the value of sample_rate and compare it to the requested rate.
 *
 * Small differences between the rates (10% or less) are normal and the
 * applications should usually tolerate them. However larger differences may
 * cause annoying pitch problems (Mickey Mouse).
 */

  return fd;
}

void
process_input (void)
{
  short buffer[1024];

  int l, i, level;

/*
 * First read a block of audio samples with proper error checking.
 */

  if ((l = read (fd_in, buffer, sizeof (buffer))) == -1)
    {
      perror ("Audio read");
      exit (-1);		/* Or return an error code */
    }

/*
 * We are using 16 bit samples so the number of bytes returned by read must be
 * converted to the number of samples (2 bytes per a 16 bit sample).
 *
 * Some care must be taken if this program is converted from 1 channels
 * (mono) to 2 channels (stereo) or more.
 *
 * Handling more than 1 channels is bit more complicated because the channels
 * are interleaved. This will be demonstrated in some other programs.
 */

  l = l / 2;

/*
 * After this point this routine will perform the peak volume computations.
 * The {!code l} variable contains the number of samples in the buffer.
 *
 * The remaining lines can be removed and replaced with the required 
 * application code.
 */

  level = 0;

  for (i = 0; i < l; i++)
    {
/*
 * Take the next sample (i) and compute it's absolute value. Check if it
 * was larger than the previous peak value.
 */
      int v = buffer[i];

      if (v < 0)
	v = -v;			/* abs */

      if (v > level)
	level = v;
    }

/*
 * Finally print the simple LED bar. The maximum value for a 16 bit
 * sample is 32*1024-1. Convert this to 32 bars.
 *
 * This program uses linear scale for simplicity. Real world audio programs
 * should probably use logarithmic scale (dB).
 */

  level = (level + 1) / 1024;

  for (i = 0; i < level; i++)
    printf ("*");
  for (i = level; i < 32; i++)
    printf (".");
  printf ("\r");
  fflush (stdout);
}

int
main (int argc, char *argv[])
{
/*
 * Use /dev/dsp as the default device because the system administrator
 * may select the device using the {!xlink ossctl} program or some other
 * methods
 */
  char *name_in = "/dev/dsp";

/*
 * It's recommended to provide some method for selecting some other
 * device than the default. We use command line argument but in some cases
 * an environment variable or some configuration file setting may be better.
 */
  if (argc > 1)
    name_in = argv[1];

/*
 * It's mandatory to use O_RDONLY in programs that do only recording. Other
 * modes may cause increased resource (memory) usage in the driver. It may
 * also prevent other applications from using the same device for 
 * playback at the same time.
 */
  fd_in = open_audio_device (name_in, O_RDONLY);

  while (1)
    process_input ();

  exit (0);
}