File: beepALSA.c

package info (click to toggle)
morse 2.4-2
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd, wheezy
  • size: 368 kB
  • ctags: 300
  • sloc: ansic: 4,032; xml: 385; makefile: 140
file content (170 lines) | stat: -rw-r--r-- 4,290 bytes parent folder | download
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
/* beepALSA.c -- Neale Pickett 8/2007 */
/* A lot of code taken from the beepOSS.c, which said: */
/* * A lot of code taken from the beepSun.c */

/*
  Implementation of beep for ALSA.
*/

#include <alsa/asoundlib.h>
#include <math.h>

static char *device = "default";

#include "beep.h"

#define RATE	(44100)        /* 44.1K samples = 1 second  (CD quality) */
#define RAMP	(RATE * 5 / 1000)                  /* 5 millisecond ramp */
#define MAXTIME	(2 * RATE)                    /* 2 seconds max tone time */

static int err;
static snd_pcm_t *handle;
static snd_pcm_sframes_t frames;
static int channels = 1; /* 1=mono, 2=stereo */
static float silence[2*MAXTIME];
static float soundbuf[2*(MAXTIME - RAMP)];
static float ramp_down[2*RAMP];

int BeepInit (void)
{
  int i;

  /*
   * Initialize the sound of silence
   */
  for (i = 0; i < 2*MAXTIME; i++) {
    silence[i] = 0;
  }

  /*
   * Set up ALSA
   */
  if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
    printf("Playback open error: %s\n", snd_strerror(err));
    return 1;                   /* Not OK */
  }
  if ((err = snd_pcm_set_params(handle,
                                SND_PCM_FORMAT_FLOAT,
                                SND_PCM_ACCESS_RW_INTERLEAVED,
                                channels,
                                RATE,
                                1,
                                500000)) < 0) {   /* 0.5sec */
    printf("Playback open error: %s\n", snd_strerror(err));
    return 1;                   /* Not OK */
  }

  BeepWait();
  return 0;                     /* OK */
}

int Beep (int time, int volume, int pitch)
{
  static int      last_pitch = -1, last_n = -1, last_volume = -1;
  int             i, n, first_len, down_len, cycle;
  double          dt;

  /*
   * Finagle the number of samples
   */
  n = (time / 1000.) * RATE;	/* Number samples in tone time */
  n = n < MAXTIME ? n : MAXTIME;	/* clip to buffer size */
  n = n < 2 * RAMP ? 2 * RAMP : n;	/* leave room for ramps */

  /*
   * Catch stupidity
   */
  if (pitch <= 0) {
    volume = 0;
  }

  if (volume <= 0) {
    snd_pcm_writei(handle, silence, channels*n);
  } else {
    /*
     * clip to Nyquist
     * (Of course this means that if you ask for too high a frequency you
     * just get silence, since you sample all the zero-crossings of the
     * sine wave.)
     */
    pitch = pitch < RATE / 2 ? pitch : RATE / 2;
    cycle = ((RATE + 1.e-6) / pitch); /* samples per cycle */

    if (cycle > MAXTIME / 2) {
      cycle = MAXTIME / 2;
    }

    /* round down length of (rampup + mesa top) to integral cycle */
    first_len = ((n - RAMP) / cycle) * cycle;
    if (first_len < cycle) {
      first_len = cycle;
    }
    if (first_len > MAXTIME - RAMP) {
      first_len = MAXTIME - RAMP;
    }

    /* round down length of (rampdown) to integral cycle */
    down_len = ((RAMP) / cycle) * cycle;
    if (down_len < cycle) {
	down_len = cycle;
    }
    if (down_len > RAMP) {
      down_len = RAMP;
    }

    /*
     * Can we just reuse what we had before?
     */
    if (pitch != last_pitch || n > last_n || volume != last_volume) {
      last_pitch = pitch;
      last_n = n;
      last_volume = volume;

      dt = 2. * M_PI / cycle;	/* sine scale factor */

      /* Ramp up; begin with silence */

      for (i = 0; i < RAMP; i++) {
        soundbuf[i*channels] = ((float)i / RAMP) * (volume/100.0) * sin(i * dt);
        if (channels == 2)
          soundbuf[i*channels+1] = soundbuf[i*channels];
      }

      /* Mesa top */
      for (i = RAMP; i < first_len; i++) {
        soundbuf[i*channels] = (volume/100.0) * sin(i * dt);
        if (channels == 2) {
          soundbuf[i*channels+1] = soundbuf[i*channels];
        }
      }

      /* Ramp down; end with silence */
      for (i = 0; i < down_len; i++) {
        ramp_down[i*channels] = (1. - ((float)i / RAMP)) * (volume/100.0) * sin(i * dt);
        if (channels == 2) {
          ramp_down[i*channels+1] = ramp_down[i*channels];
        }
      }
    }

    snd_pcm_writei(handle, soundbuf, channels*first_len);
    snd_pcm_writei(handle, ramp_down, channels*down_len);
  }

  return 0;
}

int BeepWait (void)
{
  return 0;
}

int BeepCleanup (void)
{
  return 0;
}

int BeepResume (void)
{
  return 0;
}