Skip to content

Commit 9247e7c

Browse files
committed
Split the audio functionality and added repeat
The mechanics of the ALSA audio layer have been separated out into a new file (with a view to making it more portable). Eventually I will add SDL2 audio or OSS audio to make the code more portable. In addition, I added a -r (repeat) option so it will repeat the string.
1 parent 6b089c0 commit 9247e7c

File tree

7 files changed

+181
-90
lines changed

7 files changed

+181
-90
lines changed

Makefile

+15-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,18 @@
3434
# ABSTRACT
3535
# The build system for libmorse.a.
3636
#
37-
CFLAGS= -Wall -O
37+
LIBTOOL=ar
38+
#CFLAGS= -Wall -O -DSDL2
39+
CFLAGS= -Wall -O -DALSA
3840

39-
SRCS= init.c morse.c audio.c params.c
41+
#SND_SRC=sdl2.c
42+
#SND_INC=`sdl2-config --cflags`
43+
#SND_LIB=`sdl2-config --libs`
44+
SND_SRC=alsa.c
45+
SND_INC=
46+
SND_LIB=-lasound
47+
48+
SRCS= init.c morse.c audio.c params.c $(SND_SRC)
4049
OBJS= $(SRCS:.c=.o)
4150
LIB= libmorse.a
4251

@@ -56,6 +65,9 @@ $(LIB): $(OBJS)
5665
$(AR) r $@ $?
5766

5867
morse_play: main.o $(LIB)
59-
$(CC) -o morse_play main.o -L. -lmorse -lasound -lm
68+
$(CC) -o morse_play main.o -L. -lmorse $(SND_LIB) -lm
69+
70+
sdl2.o: sdl2.c
71+
$(CC) $(CFLAGS) $(SND_INC) -c -o $@ sdl2.c
6072

6173
$(OBJS): libmorse.h

alsa.c

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (c) 2022, Kalopa Robotics Limited. All rights
3+
* reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions
7+
* are met:
8+
*
9+
* 1. Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* 2. Redistributions in binary form must reproduce the above
13+
* copyright notice, this list of conditions and the following
14+
* disclaimer in the documentation and/or other materials provided
15+
* with the distribution.
16+
*
17+
* 3. Neither the name of the copyright holder nor the names of its
18+
* contributors may be used to endorse or promote products derived
19+
* from this software without specific prior written permission.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
23+
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24+
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25+
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
26+
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
28+
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30+
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33+
*
34+
* ABSTRACT
35+
* This code handles the actual operation of creating a tone (or silence)
36+
* for morse output. It relies heavily on the ALSA sound library for the
37+
* dirty work of getting audio out.
38+
*/
39+
#include <stdio.h>
40+
#include <unistd.h>
41+
#include <stdlib.h>
42+
#include <math.h>
43+
44+
#include "libmorse.h"
45+
46+
#ifdef ALSA
47+
#include <alsa/asoundlib.h>
48+
49+
static char *device = "default";
50+
51+
/*
52+
* Initialise the ALSA library.
53+
*/
54+
void
55+
sound_open(struct morse *mp)
56+
{
57+
int err;
58+
snd_pcm_t *handle = NULL;
59+
60+
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
61+
fprintf(stderr, "libmorse init: snd_pcm_open: %s\n", snd_strerror(err));
62+
exit(1);
63+
}
64+
mp->audio = (void *)handle;
65+
}
66+
67+
/*
68+
* Just before we begin audio out, we need to set up some bits and pieces
69+
* like the audio buffer and some of the offsets. Recompute the parameters
70+
* for good measure, too.
71+
*/
72+
void
73+
sound_commence(struct morse *mp)
74+
{
75+
int err;
76+
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
77+
78+
if ((err = snd_pcm_set_params(handle,
79+
SND_PCM_FORMAT_S16_LE,
80+
SND_PCM_ACCESS_RW_INTERLEAVED,
81+
1,
82+
mp->sample_rate,
83+
1,
84+
500000)) < 0) {
85+
fprintf(stderr, "libmorse: snd_pcm_set_params: %s\n", snd_strerror(err));
86+
exit(1);
87+
}
88+
}
89+
90+
/*
91+
* Output a 16-bit audio sample. It is buffered locally and then written
92+
* whenever the buffer is full. We also track a time stamp so we know how
93+
* much audio has been written.
94+
*/
95+
void
96+
sound_out(struct morse *mp, int value)
97+
{
98+
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
99+
100+
mp->buffer[mp->offset++] = value;
101+
if (mp->offset >= AUDIO_BUFFER_SIZE) {
102+
snd_pcm_writei(handle, mp->buffer, AUDIO_BUFFER_SIZE);
103+
mp->offset = 0;
104+
}
105+
mp->time_stamp++;
106+
}
107+
108+
/*
109+
* Called prior to close. This ensure that any buffered audio is written and
110+
* we wait until the audio has actually been sent. Don't bother with this
111+
* code if you just want to exit or close down the library.
112+
*/
113+
void
114+
sound_drain(struct morse *mp)
115+
{
116+
int err;
117+
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
118+
119+
if (mp->offset > 0)
120+
snd_pcm_writei(handle, mp->buffer, mp->offset);
121+
if ((err = snd_pcm_drain(handle)) < 0)
122+
fprintf(stderr, "libmorse drain: snd_pcm_drain: %s\n", snd_strerror(err));
123+
}
124+
125+
/*
126+
* Close the library. Doesn't do much except release the ALSA audio channel.
127+
*/
128+
void
129+
sound_close(struct morse *mp)
130+
{
131+
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
132+
133+
snd_pcm_close(handle);
134+
}
135+
#endif
136+
#ifdef SDL
137+
#include <SDL_audio.h>
138+
#endif

audio.c

+2-22
Original file line numberDiff line numberDiff line change
@@ -41,28 +41,8 @@
4141
#include <stdlib.h>
4242
#include <math.h>
4343

44-
#include <alsa/asoundlib.h>
45-
4644
#include "libmorse.h"
4745

48-
/*
49-
* Output a 16-bit audio sample. It is buffered locally and then written
50-
* whenever the buffer is full. We also track a time stamp so we know how
51-
* much audio has been written.
52-
*/
53-
static void
54-
_audio_out(struct morse *mp, int value)
55-
{
56-
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
57-
58-
mp->buffer[mp->offset++] = value;
59-
if (mp->offset >= AUDIO_BUFFER_SIZE) {
60-
snd_pcm_writei(handle, mp->buffer, AUDIO_BUFFER_SIZE);
61-
mp->offset = 0;
62-
}
63-
mp->time_stamp++;
64-
}
65-
6646
/*
6747
* Generate a sinusoidal tone at the desired frequency. We use a small curve
6848
* at the end of the wave form to avoid clicks. The maths here might not be
@@ -85,7 +65,7 @@ morse_audio_tone(struct morse *mp, int len)
8565
} else
8666
value = (double )mp->word;
8767
theta = (2.0 * M_PI * (double )i * (double )mp->tone_frequency / mp->sample_rate);
88-
_audio_out(mp, (int )(value * sin(theta) + 0.5));
68+
sound_out(mp, (int )(value * sin(theta) + 0.5));
8969
}
9070
}
9171

@@ -97,7 +77,7 @@ void
9777
morse_audio_silence(struct morse *mp)
9878
{
9979
while (mp->sym_delay > 0) {
100-
_audio_out(mp, 0);
80+
sound_out(mp, 0);
10181
mp->sym_delay--;
10282
}
10383
}

init.c

+1-43
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,15 @@
3838
#include <unistd.h>
3939
#include <stdlib.h>
4040

41-
#include <alsa/asoundlib.h>
42-
4341
#include "libmorse.h"
4442

45-
static char *device = "default";
46-
4743
/*
4844
* Initialize the Morse Code library. Called with the desired words per
4945
* minute (an integer in the range of 5 <= wpm <= 60).
5046
*/
5147
struct morse *
5248
morse_init(int wpm)
5349
{
54-
int err;
55-
snd_pcm_t *handle;
5650
struct morse *mp;
5751

5852
/*
@@ -68,42 +62,6 @@ morse_init(int wpm)
6862
mp->sample_rate = 44100;
6963
mp->tone_frequency = 800.0;
7064
morse_calc_params(mp);
71-
/*
72-
* Now initialize the audio output.
73-
*/
74-
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
75-
fprintf(stderr, "libmorse init: snd_pcm_open: %s\n", snd_strerror(err));
76-
exit(1);
77-
}
78-
mp->audio = (void *)handle;
65+
sound_open(mp);
7966
return(mp);
8067
}
81-
82-
/*
83-
* Called prior to close. This ensure that any buffered audio is written and
84-
* we wait until the audio has actually been sent. Don't bother with this
85-
* code if you just want to exit or close down the library.
86-
*/
87-
void
88-
morse_drain(struct morse *mp)
89-
{
90-
int err;
91-
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
92-
93-
morse_audio_silence(mp);
94-
if (mp->offset > 0)
95-
snd_pcm_writei(handle, mp->buffer, mp->offset);
96-
if ((err = snd_pcm_drain(handle)) < 0)
97-
fprintf(stderr, "libmorse drain: snd_pcm_drain: %s\n", snd_strerror(err));
98-
}
99-
100-
/*
101-
* Close the library. Doesn't do much except release the ALSA audio channel.
102-
*/
103-
void
104-
morse_close(struct morse *mp)
105-
{
106-
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
107-
108-
snd_pcm_close(handle);
109-
}

libmorse.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,15 @@ struct morse *morse_init(int);
7878
void morse_send_char(struct morse *, int);
7979
void morse_send_word(struct morse *, char *);
8080
void morse_send_string(struct morse *, char *);
81-
void morse_drain(struct morse *);
82-
void morse_close(struct morse *);
8381
double morse_timestamp(struct morse *);
8482
void morse_calc_params(struct morse *);
8583
void morse_audio_tone(struct morse *, int);
8684
void morse_audio_silence(struct morse *);
85+
/*
86+
* Platform-specific soundcard functions.
87+
*/
88+
void sound_open(struct morse *);
89+
void sound_commence(struct morse *);
90+
void sound_out(struct morse *, int);
91+
void sound_drain(struct morse *);
92+
void sound_close(struct morse *);

main.c

+16-5
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,15 @@ void usage();
5959
int
6060
main(int argc, char *argv[])
6161
{
62-
int i, len, wpm, ampl, fw;
62+
int i, len, wpm, ampl, fw, repeat;
6363
char *str;
6464
struct morse *mp;
6565

6666
wpm = 18;
6767
opterr = fw = 0;
68+
repeat = 1;
6869
ampl = -1;
69-
while ((i = getopt(argc, argv, "a:f:s:")) != EOF) {
70+
while ((i = getopt(argc, argv, "a:f:s:r:")) != EOF) {
7071
switch (i) {
7172
case 'a':
7273
if ((ampl = atoi(optarg)) < 0 || ampl > 100) {
@@ -91,6 +92,13 @@ main(int argc, char *argv[])
9192
}
9293
break;
9394

95+
case 'r':
96+
if ((repeat = atoi(optarg)) < 1 || repeat > 100) {
97+
fprintf(stderr, "Repeat count should be between 1 and 100.\n");
98+
usage();
99+
}
100+
break;
101+
94102
default:
95103
usage();
96104
break;
@@ -117,10 +125,13 @@ main(int argc, char *argv[])
117125
strcat(str, " ");
118126
strcat(str, argv[optind]);
119127
}
120-
morse_send_string(mp, str);
121-
morse_drain(mp);
128+
for (i = 0; i < repeat; i++) {
129+
morse_send_string(mp, str);
130+
morse_audio_silence(mp);
131+
}
132+
sound_drain(mp);
122133
printf("Total time: %.2f seconds.\n", morse_timestamp(mp));
123-
morse_close(mp);
134+
sound_close(mp);
124135
exit(0);
125136
}
126137

params.c

+1-15
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@
4040
#include <unistd.h>
4141
#include <stdlib.h>
4242

43-
#include <alsa/asoundlib.h>
44-
4543
#include "libmorse.h"
4644

4745
#define MIN(a, b) ((a) < (b) ? (a) : (b))
@@ -120,22 +118,10 @@ morse_calc_params(struct morse *mp)
120118
void
121119
_morse_commence(struct morse *mp)
122120
{
123-
int err;
124-
snd_pcm_t *handle = (snd_pcm_t *)mp->audio;
125-
126121
morse_calc_params(mp);
127122
mp->sym_delay = 0;
128123
mp->time_stamp = 0;
129-
if ((err = snd_pcm_set_params(handle,
130-
SND_PCM_FORMAT_S16_LE,
131-
SND_PCM_ACCESS_RW_INTERLEAVED,
132-
1,
133-
mp->sample_rate,
134-
1,
135-
500000)) < 0) {
136-
fprintf(stderr, "libmorse: snd_pcm_set_params: %s\n", snd_strerror(err));
137-
exit(1);
138-
}
124+
sound_commence(mp);
139125
mp->buffer = (unsigned short *)malloc(AUDIO_BUFFER_SIZE * sizeof(unsigned short));
140126
if (mp->buffer == NULL) {
141127
perror("libmorse: malloc");

0 commit comments

Comments
 (0)