Cinnamon audio library
Revision | 02e48d027e034b4b95d6d0411cf0eb4580437ecd (tree) |
---|---|
Zeit | 2019-11-15 17:19:43 |
Autor | AlaskanEmily <emily@alas...> |
Commiter | AlaskanEmily |
Fix compiling on glibc Linux
@@ -7,11 +7,15 @@ include gcc.mk | ||
7 | 7 | |
8 | 8 | BACKEND?=openal |
9 | 9 | |
10 | -# Only OpenAL can be compiled without C++ currently. | |
11 | -ifeq "$(BACKEND)" "openal" | |
12 | -LINK?=$(CC) | |
10 | +ifeq "$(BACKEND)" "dsound" | |
11 | +LINK?=$(CXX) | |
12 | +else ifeq "$(BACKEND)" "oss" | |
13 | +EXTRALIBS=-lpthread | |
14 | +LINK?="$(CC)" | |
15 | +else ifeq "$(BACKEND)" "openal" | |
16 | +EXTRALIBS=-lopenal | |
17 | +LINK?="$(CC)" | |
13 | 18 | else |
14 | -LINK=$(CXX) | |
15 | 19 | endif |
16 | - | |
20 | +LINK?="$(CC)" | |
17 | 21 | include unix.mk |
@@ -1,168 +1,170 @@ | ||
1 | -/* Any copyright is dedicated to the Public Domain. | |
2 | - * http://creativecommons.org/publicdomain/zero/1.0/ | |
3 | - */ | |
4 | - | |
5 | -#ifdef _MSC_VER | |
6 | -/* Fart off, Visual C++ */ | |
7 | -#define _CRT_SECURE_NO_WARNINGS | |
8 | -#endif | |
9 | - | |
10 | -#include "cinnamon.h" | |
11 | - | |
12 | -#include <stdio.h> | |
13 | -#include <string.h> | |
14 | -#include <stdlib.h> | |
15 | - | |
16 | -/* | |
17 | - * This demonstrates the very basics of loading a sound from a file and playing | |
18 | - * it. Cinnamon is NOT a decoding library, so we load raw sounds only. You can | |
19 | - * create these with ffmpeg if you want to test. In OSS you can just use dd on | |
20 | - * the microphone file to get some data. | |
21 | - */ | |
22 | - | |
23 | -/* The platform deps are just for sleeping while the sound plays. | |
24 | - * Define AUCAT_SLEEP as a macro of arity 1 that will sleep for the argument | |
25 | - * number of milliseconds. | |
26 | - */ | |
27 | -#ifdef _WIN32 | |
28 | -/* On Windows, Sleep does exactly what we want AUCAT_SLEEP to do. */ | |
29 | - | |
30 | -#define WIN32_LEAN_AND_MEAN 1 | |
1 | +/* Any copyright is dedicated to the Public Domain. | |
2 | + * http://creativecommons.org/publicdomain/zero/1.0/ | |
3 | + */ | |
4 | + | |
5 | +#ifdef _MSC_VER | |
6 | +/* Fart off, Visual C++ */ | |
7 | +#define _CRT_SECURE_NO_WARNINGS | |
8 | +#else | |
9 | +#define _POSIX_C_SOURCE 199309L | |
10 | +#endif | |
11 | + | |
12 | +#include "cinnamon.h" | |
13 | + | |
14 | +#include <stdio.h> | |
15 | +#include <string.h> | |
16 | +#include <stdlib.h> | |
17 | + | |
18 | +/* | |
19 | + * This demonstrates the very basics of loading a sound from a file and playing | |
20 | + * it. Cinnamon is NOT a decoding library, so we load raw sounds only. You can | |
21 | + * create these with ffmpeg if you want to test. In OSS you can just use dd on | |
22 | + * the microphone file to get some data. | |
23 | + */ | |
24 | + | |
25 | +/* The platform deps are just for sleeping while the sound plays. | |
26 | + * Define AUCAT_SLEEP as a macro of arity 1 that will sleep for the argument | |
27 | + * number of milliseconds. | |
28 | + */ | |
29 | +#ifdef _WIN32 | |
30 | +/* On Windows, Sleep does exactly what we want AUCAT_SLEEP to do. */ | |
31 | + | |
32 | +#define WIN32_LEAN_AND_MEAN 1 | |
31 | 33 | #include <Windows.h> |
32 | -#define AUCAT_SLEEP Sleep | |
33 | - | |
34 | -#else | |
35 | -/* usleep is missing/deprecated on Linux, use nanosleep instead. */ | |
36 | - | |
37 | -#include <time.h> | |
38 | -#include <unistd.h> | |
39 | - | |
40 | -#define AUCAT_SLEEP(MS) do{\ | |
41 | - const unsigned long AUCAT_SLEEP_ms = (MS);\ | |
42 | - struct timespec AUCAT_SLEEP_ts;\ | |
43 | - AUCAT_SLEEP_ts.tv_nsec = (AUCAT_SLEEP_ms % 1000) * 1000000;\ | |
44 | - AUCAT_SLEEP_ts.tv_sec = AUCAT_SLEEP_ms / 1000;\ | |
45 | - nanosleep(&AUCAT_SLEEP_ts, NULL);\ | |
46 | -} while(0) | |
47 | - | |
48 | -#endif | |
49 | - | |
50 | -int main(int argc, char **argv){ | |
51 | - FILE *const input = (argc < 2) ? | |
52 | - stdin : fopen(argv[1], "rb"); | |
53 | - | |
54 | - int channels = 2; | |
55 | - int rate = 44100; | |
56 | - enum Cin_Format format = Cin_eFormatS16; | |
57 | - | |
58 | - /* Read options. */ | |
59 | - int i; | |
60 | - for(i = 2; i + 1 < argc; i++){ | |
61 | - if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--channels") == 0){ | |
62 | - channels = atoi(argv[++i]); | |
63 | - } | |
64 | - else if(strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--rate") == 0){ | |
65 | - rate = atoi(argv[++i]); | |
66 | - } | |
67 | - else if(strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--format") == 0){ | |
68 | - | |
69 | - i++; | |
70 | - if(strcmp(argv[i], "16") == 0 || strcmp(argv[i], "s16") == 0){ | |
71 | - format = Cin_eFormatS16; | |
72 | - } | |
73 | - else if(strcmp(argv[i], "8") == 0 || strcmp(argv[i], "s8") == 0){ | |
74 | - format = Cin_eFormatS8; | |
75 | - } | |
76 | - else if(strcmp(argv[i], "32") == 0 || strcmp(argv[i], "s32") == 0){ | |
77 | - format = Cin_eFormatS32; | |
78 | - } | |
79 | - else if(strcmp(argv[i], "float") == 0 || strcmp(argv[i], "float32") == 0){ | |
80 | - format = Cin_eFormatFloat32; | |
81 | - } | |
82 | - else if(strcmp(argv[i], "double") == 0 || strcmp(argv[i], "float64") == 0){ | |
83 | - format = Cin_eFormatFloat64; | |
84 | - } | |
85 | - else if(strcmp(argv[i], "u") == 0 || strcmp(argv[i], "ulaw") == 0){ | |
86 | - format = Cin_eFormatULaw8; | |
87 | - } | |
88 | - else{ | |
89 | - fprintf(stderr, "Invalid format: %s\n", argv[i]); | |
90 | - return EXIT_FAILURE; | |
91 | - } | |
92 | - } | |
93 | - } | |
94 | - | |
95 | - /* Validate options. */ | |
96 | - if(channels == 0 || channels > 8){ | |
97 | - fprintf(stderr, "Invalid number of channels: %i\n", channels); | |
98 | - return EXIT_FAILURE; | |
99 | - } | |
100 | - | |
101 | - if(rate < 8000 || rate > 0x00FFFFFF){ | |
102 | - fprintf(stderr, "Invalid sample rate: %i\n", rate); | |
103 | - return EXIT_FAILURE; | |
104 | - } | |
105 | - | |
106 | - /* Validate we could open the file. */ | |
107 | - if(input == NULL){ | |
108 | - fputs("Could not open file", stderr); | |
109 | - if(argc >= 2) | |
110 | - fputs(argv[i], stderr); | |
111 | - fputc('\n', stderr); | |
112 | - return EXIT_FAILURE; | |
113 | - } | |
114 | - | |
115 | - /* Cat the file. */ | |
116 | - { | |
117 | - char *const buffer = malloc(rate); | |
118 | - unsigned num_read, total_read = 0; | |
119 | - | |
120 | - struct Cin_Driver *const driver = malloc(Cin_StructDriverSize()); | |
121 | - struct Cin_Sound *const sound = malloc(Cin_StructSoundSize()); | |
122 | - struct Cin_Loader *const loader = malloc(Cin_StructLoaderSize()); | |
123 | - | |
124 | - if(driver == NULL || Cin_CreateDriver(driver) != Cin_eDriverSuccess){ | |
125 | - fputs("Could not create driver\n", stderr); | |
126 | - return EXIT_FAILURE; | |
127 | - } | |
128 | - | |
129 | - if(loader == NULL || Cin_CreateLoader(loader, driver, rate, channels, format) != Cin_eLoaderSuccess){ | |
130 | - fputs("Could not create loader\n", stderr); | |
131 | - return EXIT_FAILURE; | |
132 | - } | |
133 | - | |
134 | - while((num_read = fread(buffer, 1, rate, input)) != 0){ | |
135 | - Cin_LoaderPut(loader, buffer, num_read); | |
136 | - total_read += num_read; | |
137 | - } | |
138 | - | |
139 | - /* The buffer is no longer needed. */ | |
140 | - free(buffer); | |
141 | - | |
142 | - if(sound == NULL || Cin_LoaderFinalize(loader, sound) != Cin_eLoaderSuccess){ | |
143 | - fputs("Could not create sound\n", stderr); | |
144 | - return EXIT_FAILURE; | |
145 | - } | |
146 | - | |
147 | - /* Loader is totally done. */ | |
148 | - free(loader); | |
149 | - | |
150 | - /* Play the sound */ | |
151 | - Cin_SoundPlay(sound); | |
152 | - | |
153 | - /* Sleep while the sound is playing. */ | |
154 | - AUCAT_SLEEP(((total_read * 1000) + 1) / (rate * channels)); | |
155 | - | |
156 | - /* This is technically not needed, but it will catch if the sound | |
157 | - * has playback issues on some systems where DestroyDriver blocks until | |
158 | - * all sounds are complete. | |
159 | - */ | |
160 | - Cin_SoundStop(sound); | |
161 | - | |
162 | - Cin_DestroySound(sound); | |
163 | - | |
164 | - free(sound); | |
165 | - } | |
166 | - | |
167 | - return EXIT_SUCCESS; | |
168 | -} | |
34 | +#define AUCAT_SLEEP Sleep | |
35 | + | |
36 | +#else | |
37 | +/* usleep is missing/deprecated on Linux, use nanosleep instead. */ | |
38 | + | |
39 | +#include <time.h> | |
40 | +#include <unistd.h> | |
41 | + | |
42 | +#define AUCAT_SLEEP(MS) do{\ | |
43 | + const unsigned long AUCAT_SLEEP_ms = (MS);\ | |
44 | + struct timespec AUCAT_SLEEP_ts;\ | |
45 | + AUCAT_SLEEP_ts.tv_nsec = (AUCAT_SLEEP_ms % 1000) * 1000000;\ | |
46 | + AUCAT_SLEEP_ts.tv_sec = AUCAT_SLEEP_ms / 1000;\ | |
47 | + nanosleep(&AUCAT_SLEEP_ts, NULL);\ | |
48 | +} while(0) | |
49 | + | |
50 | +#endif | |
51 | + | |
52 | +int main(int argc, char **argv){ | |
53 | + FILE *const input = (argc < 2) ? | |
54 | + stdin : fopen(argv[1], "rb"); | |
55 | + | |
56 | + int channels = 2; | |
57 | + int rate = 44100; | |
58 | + enum Cin_Format format = Cin_eFormatS16; | |
59 | + | |
60 | + /* Read options. */ | |
61 | + int i; | |
62 | + for(i = 2; i + 1 < argc; i++){ | |
63 | + if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--channels") == 0){ | |
64 | + channels = atoi(argv[++i]); | |
65 | + } | |
66 | + else if(strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--rate") == 0){ | |
67 | + rate = atoi(argv[++i]); | |
68 | + } | |
69 | + else if(strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--format") == 0){ | |
70 | + | |
71 | + i++; | |
72 | + if(strcmp(argv[i], "16") == 0 || strcmp(argv[i], "s16") == 0){ | |
73 | + format = Cin_eFormatS16; | |
74 | + } | |
75 | + else if(strcmp(argv[i], "8") == 0 || strcmp(argv[i], "s8") == 0){ | |
76 | + format = Cin_eFormatS8; | |
77 | + } | |
78 | + else if(strcmp(argv[i], "32") == 0 || strcmp(argv[i], "s32") == 0){ | |
79 | + format = Cin_eFormatS32; | |
80 | + } | |
81 | + else if(strcmp(argv[i], "float") == 0 || strcmp(argv[i], "float32") == 0){ | |
82 | + format = Cin_eFormatFloat32; | |
83 | + } | |
84 | + else if(strcmp(argv[i], "double") == 0 || strcmp(argv[i], "float64") == 0){ | |
85 | + format = Cin_eFormatFloat64; | |
86 | + } | |
87 | + else if(strcmp(argv[i], "u") == 0 || strcmp(argv[i], "ulaw") == 0){ | |
88 | + format = Cin_eFormatULaw8; | |
89 | + } | |
90 | + else{ | |
91 | + fprintf(stderr, "Invalid format: %s\n", argv[i]); | |
92 | + return EXIT_FAILURE; | |
93 | + } | |
94 | + } | |
95 | + } | |
96 | + | |
97 | + /* Validate options. */ | |
98 | + if(channels == 0 || channels > 8){ | |
99 | + fprintf(stderr, "Invalid number of channels: %i\n", channels); | |
100 | + return EXIT_FAILURE; | |
101 | + } | |
102 | + | |
103 | + if(rate < 8000 || rate > 0x00FFFFFF){ | |
104 | + fprintf(stderr, "Invalid sample rate: %i\n", rate); | |
105 | + return EXIT_FAILURE; | |
106 | + } | |
107 | + | |
108 | + /* Validate we could open the file. */ | |
109 | + if(input == NULL){ | |
110 | + fputs("Could not open file", stderr); | |
111 | + if(argc >= 2) | |
112 | + fputs(argv[i], stderr); | |
113 | + fputc('\n', stderr); | |
114 | + return EXIT_FAILURE; | |
115 | + } | |
116 | + | |
117 | + /* Cat the file. */ | |
118 | + { | |
119 | + char *const buffer = malloc(rate); | |
120 | + unsigned num_read, total_read = 0; | |
121 | + | |
122 | + struct Cin_Driver *const driver = malloc(Cin_StructDriverSize()); | |
123 | + struct Cin_Sound *const sound = malloc(Cin_StructSoundSize()); | |
124 | + struct Cin_Loader *const loader = malloc(Cin_StructLoaderSize()); | |
125 | + | |
126 | + if(driver == NULL || Cin_CreateDriver(driver) != Cin_eDriverSuccess){ | |
127 | + fputs("Could not create driver\n", stderr); | |
128 | + return EXIT_FAILURE; | |
129 | + } | |
130 | + | |
131 | + if(loader == NULL || Cin_CreateLoader(loader, driver, rate, channels, format) != Cin_eLoaderSuccess){ | |
132 | + fputs("Could not create loader\n", stderr); | |
133 | + return EXIT_FAILURE; | |
134 | + } | |
135 | + | |
136 | + while((num_read = fread(buffer, 1, rate, input)) != 0){ | |
137 | + Cin_LoaderPut(loader, buffer, num_read); | |
138 | + total_read += num_read; | |
139 | + } | |
140 | + | |
141 | + /* The buffer is no longer needed. */ | |
142 | + free(buffer); | |
143 | + | |
144 | + if(sound == NULL || Cin_LoaderFinalize(loader, sound) != Cin_eLoaderSuccess){ | |
145 | + fputs("Could not create sound\n", stderr); | |
146 | + return EXIT_FAILURE; | |
147 | + } | |
148 | + | |
149 | + /* Loader is totally done. */ | |
150 | + free(loader); | |
151 | + | |
152 | + /* Play the sound */ | |
153 | + Cin_SoundPlay(sound); | |
154 | + | |
155 | + /* Sleep while the sound is playing. */ | |
156 | + AUCAT_SLEEP(((total_read * 1000) + 1) / (rate * channels)); | |
157 | + | |
158 | + /* This is technically not needed, but it will catch if the sound | |
159 | + * has playback issues on some systems where DestroyDriver blocks until | |
160 | + * all sounds are complete. | |
161 | + */ | |
162 | + Cin_SoundStop(sound); | |
163 | + | |
164 | + Cin_DestroySound(sound); | |
165 | + | |
166 | + free(sound); | |
167 | + } | |
168 | + | |
169 | + return EXIT_SUCCESS; | |
170 | +} |
@@ -7,11 +7,10 @@ | ||
7 | 7 | |
8 | 8 | BACKEND?=oss |
9 | 9 | |
10 | -# Only OpenAL can be compiled without C++ currently. | |
11 | -.if "${BACKEND}" == "openal" | |
12 | -LINK?=$(CC) | |
10 | +.if "${BACKEND}" == "dsound" | |
11 | +LINK?=$(CXX) | |
13 | 12 | .else |
14 | -LINK=$(CXX) | |
13 | +LINK=$(CC) | |
15 | 14 | .endif |
16 | 15 | |
17 | 16 | .include "unix.mk" |
@@ -27,10 +27,10 @@ aucat.o: aucat.c cinnamon.h cin_export.h cin_format.h | ||
27 | 27 | $(CC) $(CFLAGS) -c aucat.c -o aucat.o |
28 | 28 | |
29 | 29 | sine_test: libcinnamon.a sine_test.o |
30 | - $(LINK) sine_test.o libcinnamon.a -o sine_test | |
30 | + $(LINK) sine_test.o libcinnamon.a $(EXTRALIBS) -lm -o sine_test | |
31 | 31 | |
32 | 32 | aucat: libcinnamon.a aucat.o |
33 | - $(LINK) aucat.o libcinnamon.a -o aucat | |
33 | + $(LINK) aucat.o libcinnamon.a $(EXTRALIBS) -o aucat | |
34 | 34 | |
35 | 35 | clean: |
36 | 36 | rm *.o 2> /dev/null || true |