Cinnamon audio library
Revision | bf3ed9a4403b7797fd6426f3946fa2ae3c6c65b9 (tree) |
---|---|
Zeit | 2019-01-22 10:05:52 |
Autor | AlaskanEmily <emily@alas...> |
Commiter | AlaskanEmily |
Major refactor of source location. Remove unused DSP components for now. Change all DirectSound sounds to use full buffering.
@@ -0,0 +1,176 @@ | ||
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 <stdlib.h> | |
14 | + | |
15 | +/* | |
16 | + * This demonstrates the very basics of loading a sound from a file and playing | |
17 | + * it. Cinnamon is NOT a decoding library, so we load raw sounds only. You can | |
18 | + * create these with ffmpeg if you want to test. In OSS you can just use dd on | |
19 | + * the microphone file to get some data. | |
20 | + */ | |
21 | + | |
22 | +/* The platform deps are just for sleeping while the sound plays. | |
23 | + * Define AUCAT_SLEEP as a macro of arity 1 that will sleep for the argument | |
24 | + * number of milliseconds. | |
25 | + */ | |
26 | +#ifdef _WIN32 | |
27 | +/* On Windows, Sleep does exactly what we want AUCAT_SLEEP to do. */ | |
28 | + | |
29 | +#define WIN32_LEAN_AND_MEAN 1 | |
30 | +#include <Windows.h> | |
31 | +#define AUCAT_SLEEP Sleep | |
32 | + | |
33 | +#elif defined __linux__ | |
34 | +/* usleep is missing/deprecated on Linux, use nanosleep instead. */ | |
35 | + | |
36 | +#include <time.h> | |
37 | +#include <unistd.h> | |
38 | + | |
39 | +#define AUCAT_SLEEP(MS) do{\ | |
40 | + const unsigned long AUCAT_SLEEP_ms = (MS);\ | |
41 | + struct AUCAT_SLEEP_ts;\ | |
42 | + AUCAT_SLEEP_ts.tn_sec = (AUCAT_SLEEP_ms % 1000) * 1000000;\ | |
43 | + AUCAT_SLEEP_ts.tv_sec = AUCAT_SLEEP_ms / 1000;\ | |
44 | + nanosleep(&AUCAT_SLEEP_ts, NULL);\ | |
45 | +} while(0) | |
46 | + | |
47 | +#else | |
48 | +/* On BSD and OS X (and likely other platforms like Solaris and AIX) we can | |
49 | + * simply use nanosleep. This requires _BSD_SOURCE on some platforms. | |
50 | + */ | |
51 | + | |
52 | +#define _BSD_SOURCE | |
53 | +#include <time.h> | |
54 | +#include <unistd.h> | |
55 | + | |
56 | +#define AUCAT_SLEEP(MS) do{ nanosleep((MS) * 1000); }while(0) | |
57 | + | |
58 | +#endif | |
59 | + | |
60 | +int main(int argc, char **argv){ | |
61 | + FILE *const input = (argc < 2) ? | |
62 | + stdin : fopen(argv[1], "rb"); | |
63 | + | |
64 | + int channels = 2; | |
65 | + int rate = 44100; | |
66 | + enum Cin_Format format = Cin_eFormatS16; | |
67 | + | |
68 | + /* Read options. */ | |
69 | + int i; | |
70 | + for(i = 2; i + 1 < argc; i++){ | |
71 | + if(strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--channels") == 0){ | |
72 | + channels = atoi(argv[++i]); | |
73 | + } | |
74 | + else if(strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--rate") == 0){ | |
75 | + rate = atoi(argv[++i]); | |
76 | + } | |
77 | + else if(strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--format") == 0){ | |
78 | + | |
79 | + i++; | |
80 | + if(strcmp(argv[i], "16") == 0 || strcmp(argv[i], "s16") == 0){ | |
81 | + format = Cin_eFormatS16; | |
82 | + } | |
83 | + else if(strcmp(argv[i], "8") == 0 || strcmp(argv[i], "s8") == 0){ | |
84 | + format = Cin_eFormatS8; | |
85 | + } | |
86 | + else if(strcmp(argv[i], "32") == 0 || strcmp(argv[i], "s32") == 0){ | |
87 | + format = Cin_eFormatS32; | |
88 | + } | |
89 | + else if(strcmp(argv[i], "float") == 0 || strcmp(argv[i], "float32") == 0){ | |
90 | + format = Cin_eFormatFloat32; | |
91 | + } | |
92 | + else if(strcmp(argv[i], "double") == 0 || strcmp(argv[i], "float64") == 0){ | |
93 | + format = Cin_eFormatFloat64; | |
94 | + } | |
95 | + else if(strcmp(argv[i], "u") == 0 || strcmp(argv[i], "ulaw") == 0){ | |
96 | + format = Cin_eFormatULaw8; | |
97 | + } | |
98 | + else{ | |
99 | + fprintf(stderr, "Invalid format: %s\n", argv[i]); | |
100 | + return EXIT_FAILURE; | |
101 | + } | |
102 | + } | |
103 | + } | |
104 | + | |
105 | + /* Validate options. */ | |
106 | + if(channels == 0 || channels > 8){ | |
107 | + fprintf(stderr, "Invalid number of channels: %i\n", channels); | |
108 | + return EXIT_FAILURE; | |
109 | + } | |
110 | + | |
111 | + if(rate < 8000 || rate > 0x00FFFFFF){ | |
112 | + fprintf(stderr, "Invalid sample rate: %i\n", rate); | |
113 | + return EXIT_FAILURE; | |
114 | + } | |
115 | + | |
116 | + /* Validate we could open the file. */ | |
117 | + if(input == NULL){ | |
118 | + fputs("Could not open file", stderr); | |
119 | + if(argc >= 2) | |
120 | + fputs(argv[i], stderr); | |
121 | + fputc('\n', stderr); | |
122 | + return EXIT_FAILURE; | |
123 | + } | |
124 | + | |
125 | + /* Cat the file. */ | |
126 | + { | |
127 | + char *const buffer = malloc(rate); | |
128 | + unsigned num_read, total_read = 0; | |
129 | + | |
130 | + struct Cin_Driver *const driver = malloc(Cin_StructDriverSize()); | |
131 | + struct Cin_Sound *const sound = malloc(Cin_StructSoundSize()); | |
132 | + struct Cin_Loader *const loader = malloc(Cin_StructLoaderSize()); | |
133 | + | |
134 | + if(driver == NULL || Cin_CreateDriver(driver) != Cin_eDriverSuccess){ | |
135 | + fputs("Could not create driver\n", stderr); | |
136 | + return EXIT_FAILURE; | |
137 | + } | |
138 | + | |
139 | + if(loader == NULL || Cin_CreateLoader(loader, driver, rate, channels, format) != Cin_eLoaderSuccess){ | |
140 | + fputs("Could not create loader\n", stderr); | |
141 | + return EXIT_FAILURE; | |
142 | + } | |
143 | + | |
144 | + while(num_read = fread(buffer, 1, rate, input)){ | |
145 | + Cin_LoaderPut(loader, buffer, num_read); | |
146 | + total_read += num_read; | |
147 | + } | |
148 | + | |
149 | + /* The buffer is no longer needed. */ | |
150 | + free(buffer); | |
151 | + | |
152 | + if(sound == NULL || Cin_LoaderFinalize(loader, sound) != Cin_eLoaderSuccess){ | |
153 | + fputs("Could not create sound\n", stderr); | |
154 | + return EXIT_FAILURE; | |
155 | + } | |
156 | + | |
157 | + /* Loader is totally done. */ | |
158 | + free(loader); | |
159 | + | |
160 | + /* Play the sound */ | |
161 | + Cin_SoundPlay(sound); | |
162 | + | |
163 | + /* Sleep while the sound is playing. */ | |
164 | + AUCAT_SLEEP(((total_read * 1000) + 1) / (rate * channels)); | |
165 | + | |
166 | + /* This is technically not needed, but it will catch if the sound | |
167 | + * has playback issues on some systems where DestroyDriver blocks until | |
168 | + * all sounds are complete. | |
169 | + */ | |
170 | + Cin_SoundStop(sound); | |
171 | + | |
172 | + Cin_DestroySound(sound); | |
173 | + | |
174 | + free(sound); | |
175 | + } | |
176 | +} |
@@ -0,0 +1,71 @@ | ||
1 | +/* | |
2 | + * Any copyright is dedicated to the Public Domain. | |
3 | + * http://creativecommons.org/publicdomain/zero/1.0/ | |
4 | + */ | |
5 | + | |
6 | +#ifndef CIN_EXPORT_H | |
7 | +#define CIN_EXPORT_H | |
8 | +#pragma once | |
9 | + | |
10 | +#if ( defined _WIN32 ) && !( defined __GNUC__ ) | |
11 | + | |
12 | + /* For a DLL build, use dllimport. Since we use a DEF file, we do not need | |
13 | + * to use dllexport. */ | |
14 | + #if (!defined(CIN_STATIC)) && !defined CIN_INTERNAL | |
15 | + | |
16 | + #define CIN_DLL_EXPORT(X) __declspec(dllimport) X | |
17 | + | |
18 | + #else /* CIN_DLL */ | |
19 | + | |
20 | + #define CIN_DLL_EXPORT(X) X | |
21 | + | |
22 | + #endif /* CIN_DLL */ | |
23 | + | |
24 | + /* Use the C calling convention for all exported symbols. */ | |
25 | + #define CIN_EXPORT(X) CIN_DLL_EXPORT(X) __cdecl | |
26 | + | |
27 | + /* We want to use fastcall for non-Watcom on internal functions. */ | |
28 | + #ifdef __WATCOMC__ | |
29 | + | |
30 | + #define CIN_PRIVATE(X) X | |
31 | + | |
32 | + #else | |
33 | + | |
34 | + #define CIN_PRIVATE(X) X __fastcall | |
35 | + | |
36 | + #endif | |
37 | + | |
38 | +#elif defined __GNUC__ /* GCC or not WIN32 */ | |
39 | + | |
40 | +#ifdef __CYGWIN__ | |
41 | + /* Use the C calling convention for all exported symbols, with protected visibility. */ | |
42 | + #define CIN_EXPORT(X) X __attribute__((visibility ("default"), cdecl, used)) | |
43 | + /* Internal functions have hidden visibility. */ | |
44 | + #define CIN_PRIVATE(X) X __attribute__((visibility("hidden"), fastcall)) | |
45 | +#else | |
46 | + #define CIN_EXPORT(X) X __attribute__((visibility ("default"))) | |
47 | + #define CIN_PRIVATE(X) X __attribute__((visibility("hidden"))) | |
48 | +#endif | |
49 | + | |
50 | +#else /* not WIN32 and not GCC */ | |
51 | + | |
52 | + #define CIN_EXPORT(X) X | |
53 | + #define CIN_PRIVATE(X) X | |
54 | + | |
55 | +#endif | |
56 | + | |
57 | +#ifdef __GNUC__ | |
58 | + | |
59 | +#define CIN_PRIVATE_PURE(X) CIN_PRIVATE(X) __attribute__((const)) | |
60 | + | |
61 | +#elif defined _MSC_VER | |
62 | + | |
63 | +#define CIN_PRIVATE_PURE(X) __declspec(noalias) CIN_PRIVATE(X) | |
64 | + | |
65 | +#else | |
66 | + | |
67 | +#define CIN_PRIVATE_PURE(X) CIN_PRIVATE(X) | |
68 | + | |
69 | +#endif | |
70 | + | |
71 | +#endif /* CIN_EXPORT_H */ |
@@ -0,0 +1,176 @@ | ||
1 | +/* Copyright (c) 2018-2019 Alaskan Emily, Transnat Games | |
2 | + * | |
3 | + * This Source Code Form is subject to the terms of the Mozilla Public | |
4 | + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
5 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
6 | + */ | |
7 | + | |
8 | +#ifndef CINNAMON_H | |
9 | +#define CINNAMON_H | |
10 | +#pragma once | |
11 | + | |
12 | +#include "cin_export.h" | |
13 | + | |
14 | +#ifdef __cplusplus | |
15 | +extern "C" { | |
16 | +#endif | |
17 | + | |
18 | +enum Cin_Format{ | |
19 | + Cin_eFormatS8, | |
20 | + Cin_eFormatS16, | |
21 | + Cin_eFormatS32, | |
22 | + Cin_eFormatFloat32, | |
23 | + Cin_eFormatFloat64, | |
24 | + Cin_eFormatULaw8, | |
25 | + Cin_eFormatNUM_FORMATS, | |
26 | + | |
27 | + Cin_eFormatBEGIN = 0, | |
28 | + Cin_eFormatEND = (Cin_eFormatNUM_FORMATS - 1) | |
29 | +}; | |
30 | + | |
31 | +#define CIN_FORMAT_BYTES_PER_SAMPLE(FMT) ( \ | |
32 | + ((FMT) == Cin_eFormatS8 || (FMT) == Cin_eFormatULaw8) ? 1 : \ | |
33 | + ((FMT) == Cin_eFormatS16) ? 2 : \ | |
34 | + ((FMT) == Cin_eFormatS32 || (FMT) == Cin_eFormatFloat32) ? 4 : \ | |
35 | + ((FMT) == Cin_eFormatFloat64) ? 8 : 0 \ | |
36 | + ) | |
37 | + | |
38 | +/** | |
39 | + * @defgroup Driver Functions that create, destroy, and manipulate the Driver. | |
40 | + * @{ | |
41 | + */ | |
42 | + | |
43 | +struct Cin_Driver; | |
44 | + | |
45 | +/** | |
46 | + * @brief Errors that occur when creating or manipulating a Driver. | |
47 | + */ | |
48 | +enum Cin_DriverError { | |
49 | + Cin_eDriverSuccess, /**< No error occured */ | |
50 | + Cin_eDriverFailure, | |
51 | + Cin_eDriverUnsupportedFormat, /**< The specified format is not supported */ | |
52 | + Cin_eDriverInvalidFormat, /**< The specified format is invalid */ | |
53 | + Cin_eDriverUnsupportedNumChannels, /**< Unsupported number of channels */ | |
54 | + Cin_eDriverUnsupportedSampleRate, /**< Unsupported sample rate */ | |
55 | + Cin_eDriverNoDevice | |
56 | +}; | |
57 | + | |
58 | +/** | |
59 | + * @brief Returns the size of struct Cin_Driver | |
60 | + * | |
61 | + * The client is expected to allocate space for the driver. | |
62 | + */ | |
63 | +CIN_EXPORT(unsigned) Cin_StructDriverSize(void); | |
64 | + | |
65 | +/** | |
66 | + * @brief Initializes a Driver. | |
67 | + * | |
68 | + * The client is expected to allocate space for the driver. Use | |
69 | + * Cin_StructDriverSize to get the size of the driver struct. | |
70 | + * | |
71 | + * @warning The data that is placed in drv must NOT be copied. Do not use | |
72 | + * memcpy on @p sdrv, other structures may depend on the address of @p drv. | |
73 | + * | |
74 | + * @sa Cin_StructDriverSize | |
75 | + * @sa Cin_DestroyDriver | |
76 | + * @todo There is currently no way to enumerate devices. | |
77 | + */ | |
78 | +CIN_EXPORT(enum Cin_DriverError) Cin_CreateDriver(struct Cin_Driver *drv); | |
79 | + | |
80 | +/** | |
81 | + * @brief Destroys the Driver | |
82 | + * | |
83 | + * As the storage space for @p drv was allocated by the client, they must also | |
84 | + * free the space after use. | |
85 | + */ | |
86 | +CIN_EXPORT(void) Cin_DestroyDriver(struct Cin_Driver *drv); | |
87 | + | |
88 | +/** | |
89 | + * @brief Returns if the Driver supports creating loaders for a format. | |
90 | + */ | |
91 | +CIN_EXPORT(enum Cin_DriverError) Cin_DriverSupportsFormat( | |
92 | + const struct Cin_Driver *drv, | |
93 | + enum Cin_Format format, | |
94 | + unsigned num_channels); | |
95 | + | |
96 | +CIN_EXPORT(enum Cin_DriverError) Cin_DriverSupportsSampleRate( | |
97 | + const struct Cin_Driver *drv, | |
98 | + unsigned rate); | |
99 | + | |
100 | +/** @} */ /* End Driver group. */ | |
101 | + | |
102 | +/** | |
103 | + * @defgroup Sound Functions that play, stop, and destroy Sounds. | |
104 | + * @{ | |
105 | + */ | |
106 | + | |
107 | +struct Cin_Sound; | |
108 | + | |
109 | +/** | |
110 | + * @brief Errors that occur when manipulating a Sound. | |
111 | + */ | |
112 | +enum Cin_SoundError { | |
113 | + Cin_eSoundSuccess, /**< No error occured */ | |
114 | + Cin_eSoundFailure | |
115 | +}; | |
116 | + | |
117 | +/** | |
118 | + * @brief Returns the size of struct Cin_Sound | |
119 | + * | |
120 | + * The client is expected to allocate space for the Sound. | |
121 | + */ | |
122 | +CIN_EXPORT(unsigned) Cin_StructSoundSize(); | |
123 | + | |
124 | +CIN_EXPORT(enum Cin_SoundError) Cin_SoundPlay(struct Cin_Sound *snd); | |
125 | + | |
126 | +CIN_EXPORT(enum Cin_SoundError) Cin_SoundStop(struct Cin_Sound *snd); | |
127 | + | |
128 | +CIN_EXPORT(void) Cin_DestroySound(struct Cin_Sound *snd); | |
129 | + | |
130 | +/** @} */ /* End Sound group. */ | |
131 | + | |
132 | +/** | |
133 | + * @defgroup Loader Functions that create, put data on, and finalize Loaders. | |
134 | + * @{ | |
135 | + */ | |
136 | + | |
137 | +struct Cin_Loader; | |
138 | + | |
139 | +/** | |
140 | + * @brief Errors that occur when manipulating a Loader. | |
141 | + */ | |
142 | +enum Cin_LoaderError { | |
143 | + Cin_eLoaderSuccess, /**< No error occured */ | |
144 | + Cin_eLoaderFailure, /**< An unspecified error occured */ | |
145 | + Cin_eLoaderUnsupportedFormat, /**< The specified format is not supported */ | |
146 | + Cin_eLoaderInvalidFormat, /**< The specified format is invalid */ | |
147 | + Cin_eLoaderUnsupportedNumChannels, /**< Unsupported number of channels */ | |
148 | + Cin_eLoaderUnsupportedSampleRate /**< Unsupported sample rate */ | |
149 | +}; | |
150 | + | |
151 | +/** | |
152 | + * @brief Returns the size of struct Cin_Loader | |
153 | + * | |
154 | + * The client is expected to allocate space for the Loader. | |
155 | + */ | |
156 | +CIN_EXPORT(unsigned) Cin_StructLoaderSize(); | |
157 | + | |
158 | +CIN_EXPORT(enum Cin_LoaderError) Cin_CreateLoader(struct Cin_Loader *out, | |
159 | + struct Cin_Driver *drv, | |
160 | + unsigned sample_rate, | |
161 | + unsigned channels, | |
162 | + enum Cin_Format format); | |
163 | + | |
164 | +CIN_EXPORT(enum Cin_LoaderError) Cin_LoaderPut(struct Cin_Loader *ld, | |
165 | + const void *data, | |
166 | + unsigned byte_size); | |
167 | + | |
168 | +CIN_EXPORT(enum Cin_LoaderError) Cin_LoaderFinalize(struct Cin_Loader *ld, | |
169 | + struct Cin_Sound *out); | |
170 | + | |
171 | +/** @} */ /* End Loader group. */ | |
172 | +#ifdef __cplusplus | |
173 | +} // extern "C" | |
174 | +#endif | |
175 | + | |
176 | +#endif /* CIN_SOUND_H */ |
@@ -0,0 +1,106 @@ | ||
1 | +#include "cin_soft_loader.h" | |
2 | +#include "cinnamon.h" | |
3 | + | |
4 | +#include <assert.h> | |
5 | +#include <stdlib.h> | |
6 | +#include <string.h> | |
7 | + | |
8 | +unsigned Cin_StructLoaderSize(){ | |
9 | + return sizeof(struct Cin_Loader); | |
10 | +} | |
11 | + | |
12 | +CIN_PRIVATE(void) Cin_CreateSoftLoader(struct Cin_Loader *out, | |
13 | + unsigned sample_rate, | |
14 | + unsigned channels, | |
15 | + enum Cin_Format format){ | |
16 | + | |
17 | + assert(out != NULL); | |
18 | + | |
19 | + out->sample_rate = sample_rate; | |
20 | + out->channels = channels; | |
21 | + out->format = format; | |
22 | + out->bytes_placed = 0; | |
23 | + out->first = out->last = NULL; | |
24 | +} | |
25 | + | |
26 | +enum Cin_LoaderError Cin_LoaderPut(struct Cin_Loader *ld, | |
27 | + const void *data, | |
28 | + unsigned byte_size){ | |
29 | + | |
30 | + struct Cin_LoaderData *const lddata = | |
31 | + malloc(sizeof(struct Cin_LoaderData) + byte_size); | |
32 | + | |
33 | + assert(lddata != NULL); | |
34 | + | |
35 | + lddata->next = NULL; | |
36 | + lddata->len = byte_size; | |
37 | + | |
38 | + memcpy(lddata + 1, data, byte_size); | |
39 | + | |
40 | + if(ld->first == NULL){ | |
41 | + ld->first = ld->last = lddata; | |
42 | + } | |
43 | + else{ | |
44 | + ld->last->next = lddata; | |
45 | + ld->last = lddata; | |
46 | + } | |
47 | + | |
48 | + ld->bytes_placed += byte_size; | |
49 | + | |
50 | + return Cin_eLoaderSuccess; | |
51 | +} | |
52 | + | |
53 | +CIN_PRIVATE(void) Cin_LoaderFreeData(struct Cin_LoaderData *data){ | |
54 | + if(data == NULL){ | |
55 | + return; | |
56 | + } | |
57 | + else{ | |
58 | + struct Cin_LoaderData *const next = data->next; | |
59 | + free(data); | |
60 | + Cin_LoaderFreeData(next); | |
61 | + } | |
62 | +} | |
63 | + | |
64 | +CIN_PRIVATE(void) Cin_LoaderMemcpy(const struct Cin_LoaderData *data, | |
65 | + unsigned at, | |
66 | + void *dest, | |
67 | + unsigned count){ | |
68 | + | |
69 | + const unsigned len = data->len; | |
70 | + unsigned char *const dest_bytes = (unsigned char *)dest; | |
71 | + const struct Cin_LoaderData *const next = data->next; | |
72 | + | |
73 | + if(at > len){ | |
74 | + /* Find the first data block with the area we care about. */ | |
75 | + if(next) | |
76 | + Cin_LoaderMemcpy(next, at - data->len, dest, count); | |
77 | + } | |
78 | + else if(at > 0){ | |
79 | + const unsigned remaining = len - at; | |
80 | + if(remaining >= count){ | |
81 | + /* Copy out the partial area we care about */ | |
82 | + memcpy(dest, data->data + at, count); | |
83 | + } | |
84 | + else{ | |
85 | + /* Start our copy from the input index. */ | |
86 | + memcpy(dest, data->data + at, remaining); | |
87 | + if(next) | |
88 | + Cin_LoaderMemcpy(next, | |
89 | + 0, | |
90 | + dest_bytes + remaining, | |
91 | + count - remaining); | |
92 | + } | |
93 | + } | |
94 | + else{ | |
95 | + /* Copy out the max of our len and the count */ | |
96 | + if(len >= count){ | |
97 | + /* This data holds the remaining count. */ | |
98 | + memcpy(dest, data->data, count); | |
99 | + } | |
100 | + else{ | |
101 | + memcpy(dest, data->data, len); | |
102 | + if(next) | |
103 | + Cin_LoaderMemcpy(next, 0, dest_bytes + len, count - len); | |
104 | + } | |
105 | + } | |
106 | +} |
@@ -0,0 +1,74 @@ | ||
1 | +/* Copyright (c) 2018 AlaskanEmily | |
2 | + * | |
3 | + * This Source Code Form is subject to the terms of the Mozilla Public | |
4 | + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
5 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
6 | + */ | |
7 | + | |
8 | +#ifndef CIN_LOADER_SOFT_H | |
9 | +#define CIN_LOADER_SOFT_H | |
10 | +#pragma once | |
11 | + | |
12 | +#include "cin_export.h" | |
13 | +#include "cinnamon.h" | |
14 | + | |
15 | +#ifdef __cplusplus | |
16 | +extern "C" { | |
17 | +#endif | |
18 | + | |
19 | +/* Implementation of a loader in software only. */ | |
20 | + | |
21 | +#ifdef _MSC_VER | |
22 | +/* Push the warning about the data[] field */ | |
23 | +#pragma warning(push) | |
24 | +#pragma warning(disable: 4200) | |
25 | +#endif | |
26 | + | |
27 | +/*****************************************************************************/ | |
28 | + | |
29 | +struct Cin_LoaderData { | |
30 | + struct Cin_LoaderData *next; | |
31 | + unsigned len; | |
32 | + unsigned char data[]; | |
33 | +}; | |
34 | + | |
35 | +/*****************************************************************************/ | |
36 | + | |
37 | +#ifdef _MSC_VER | |
38 | +#pragma warning(pop) | |
39 | +#endif | |
40 | + | |
41 | +/*****************************************************************************/ | |
42 | + | |
43 | +struct Cin_Loader{ | |
44 | + unsigned sample_rate; | |
45 | + unsigned channels; | |
46 | + enum Cin_Format format; | |
47 | + struct Cin_LoaderData *first, *last; | |
48 | + unsigned bytes_placed; | |
49 | + void *data; /**< Data placed here by the backend. */ | |
50 | +}; | |
51 | + | |
52 | +/*****************************************************************************/ | |
53 | + | |
54 | +CIN_PRIVATE(void) Cin_LoaderFreeData(struct Cin_LoaderData *data); | |
55 | + | |
56 | +/*****************************************************************************/ | |
57 | + | |
58 | +CIN_PRIVATE(void) Cin_LoaderMemcpy(const struct Cin_LoaderData *data, | |
59 | + unsigned at, | |
60 | + void *dest, | |
61 | + unsigned count); | |
62 | + | |
63 | +/*****************************************************************************/ | |
64 | + | |
65 | +CIN_PRIVATE(void) Cin_CreateSoftLoader(struct Cin_Loader *out, | |
66 | + unsigned sample_rate, | |
67 | + unsigned channels, | |
68 | + enum Cin_Format format); | |
69 | + | |
70 | +#ifdef __cplusplus | |
71 | +} // extern "C" | |
72 | +#endif | |
73 | + | |
74 | +#endif /* CIN_LOADER_SOFT_H */ |
@@ -0,0 +1,12 @@ | ||
1 | +# Any copyright is dedicated to the Public Domain. | |
2 | +# http://creativecommons.org/publicdomain/zero/1.0/ | |
3 | + | |
4 | +all: cin_common.lib | |
5 | + | |
6 | +OBJECTS=cin_soft_loader.obj | |
7 | + | |
8 | +cin_common.lib: $(OBJECTS) | |
9 | + lib $(LIBFLAGS) /OUT:cin_common.lib $(OBJECTS) | |
10 | + | |
11 | +cin_soft_loader.obj: cin_soft_loader.c cin_soft_loader.h | |
12 | + cl $(CLFLAGS) cin_soft_loader.c |
@@ -0,0 +1,98 @@ | ||
1 | +// Copyright (c) 2018 AlaskanEmily | |
2 | +// This Source Code Form is subject to the terms of the Mozilla Public | |
3 | +// License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 | +// file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 | + | |
6 | +#include "cin_dsound_driver.hpp" | |
7 | +#include "cin_dsound_sound.hpp" | |
8 | +#include "cin_soft_loader.h" | |
9 | +#include "cinnamon.h" | |
10 | + | |
11 | +#include <new> | |
12 | + | |
13 | +/////////////////////////////////////////////////////////////////////////////// | |
14 | + | |
15 | +CIN_EXPORT(unsigned) Cin_StructDriverSize(void){ | |
16 | + return sizeof(Cin_Driver); | |
17 | +} | |
18 | + | |
19 | +/////////////////////////////////////////////////////////////////////////////// | |
20 | + | |
21 | +CIN_EXPORT(enum Cin_DriverError) Cin_CreateDriver(struct Cin_Driver *drv){ | |
22 | + new (drv) Cin_Driver(); | |
23 | + return Cin_eDriverSuccess; | |
24 | +} | |
25 | + | |
26 | +/////////////////////////////////////////////////////////////////////////////// | |
27 | + | |
28 | +CIN_EXPORT(void) Cin_DestroyDriver(struct Cin_Driver *drv){ | |
29 | + drv->~Cin_Driver(); | |
30 | +} | |
31 | + | |
32 | +/////////////////////////////////////////////////////////////////////////////// | |
33 | + | |
34 | +CIN_EXPORT(enum Cin_DriverError) Cin_DriverSupportsFormat( | |
35 | + const struct Cin_Driver *drv, | |
36 | + enum Cin_Format format, | |
37 | + unsigned num_channels){ | |
38 | + | |
39 | + // TODO! | |
40 | + return Cin_eDriverSuccess; | |
41 | +} | |
42 | + | |
43 | +/////////////////////////////////////////////////////////////////////////////// | |
44 | + | |
45 | +CIN_EXPORT(enum Cin_DriverError) Cin_DriverSupportsSampleRate( | |
46 | + const struct Cin_Driver *drv, | |
47 | + unsigned rate){ | |
48 | + | |
49 | + // TODO! | |
50 | + return Cin_eDriverSuccess; | |
51 | +} | |
52 | + | |
53 | +/////////////////////////////////////////////////////////////////////////////// | |
54 | + | |
55 | +CIN_EXPORT(unsigned) Cin_StructSoundSize(){ | |
56 | + return sizeof(Cin_Sound); | |
57 | +} | |
58 | + | |
59 | +/////////////////////////////////////////////////////////////////////////////// | |
60 | + | |
61 | +CIN_EXPORT(enum Cin_SoundError) Cin_SoundPlay(Cin_Sound *snd){ | |
62 | + snd->play(false); | |
63 | + return Cin_eSoundSuccess; | |
64 | +} | |
65 | + | |
66 | +/////////////////////////////////////////////////////////////////////////////// | |
67 | + | |
68 | +CIN_EXPORT(enum Cin_SoundError) Cin_SoundStop(Cin_Sound *snd){ | |
69 | + snd->stop(); | |
70 | + return Cin_eSoundSuccess; | |
71 | +} | |
72 | + | |
73 | +/////////////////////////////////////////////////////////////////////////////// | |
74 | + | |
75 | +CIN_EXPORT(void) Cin_DestroySound(Cin_Sound *snd){ | |
76 | + snd->~Cin_Sound(); | |
77 | +} | |
78 | + | |
79 | +/////////////////////////////////////////////////////////////////////////////// | |
80 | + | |
81 | +CIN_EXPORT(enum Cin_LoaderError) Cin_CreateLoader(Cin_Loader *out, | |
82 | + Cin_Driver *drv, | |
83 | + unsigned sample_rate, | |
84 | + unsigned channels, | |
85 | + enum Cin_Format format){ | |
86 | + Cin_CreateSoftLoader(out, sample_rate, channels, format); | |
87 | + out->data = drv; | |
88 | + return Cin_eLoaderSuccess; | |
89 | +} | |
90 | + | |
91 | +/////////////////////////////////////////////////////////////////////////////// | |
92 | + | |
93 | +CIN_EXPORT(enum Cin_LoaderError) Cin_LoaderFinalize(Cin_Loader *ld, Cin_Sound *out){ | |
94 | + Cin_Driver *drv = static_cast<Cin_Driver*>(ld->data); | |
95 | + drv->createSound(out, *ld); | |
96 | + Cin_LoaderFreeData(ld->first); | |
97 | + return Cin_eLoaderSuccess; | |
98 | +} |
@@ -0,0 +1,32 @@ | ||
1 | +// Copyright (c) 2018 AlaskanEmily | |
2 | +// This Source Code Form is subject to the terms of the Mozilla Public | |
3 | +// License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 | +// file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 | + | |
6 | +#include "cin_dsound_driver.hpp" | |
7 | +#include "cin_dsound_sound.hpp" | |
8 | + | |
9 | +#include <new> | |
10 | + | |
11 | +/////////////////////////////////////////////////////////////////////////////// | |
12 | + | |
13 | +Cin_Driver::Cin_Driver(){ | |
14 | + CoInitialize(NULL); | |
15 | + DirectSoundCreate8(NULL, &m_dsound, NULL); | |
16 | + m_dsound->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY); | |
17 | +} | |
18 | + | |
19 | +/////////////////////////////////////////////////////////////////////////////// | |
20 | + | |
21 | +Cin_Driver::~Cin_Driver(){ | |
22 | + m_dsound->Release(); | |
23 | +} | |
24 | + | |
25 | +/////////////////////////////////////////////////////////////////////////////// | |
26 | + | |
27 | +void Cin_Driver::createSound(Cin_Sound *out, const Cin_Loader &ld){ | |
28 | + new (out) Cin_Sound(m_dsound, ld); | |
29 | +} | |
30 | + | |
31 | +/////////////////////////////////////////////////////////////////////////////// | |
32 | +// API functions |
@@ -0,0 +1,42 @@ | ||
1 | +// Copyright (c) 2018 AlaskanEmily | |
2 | +// This Source Code Form is subject to the terms of the Mozilla Public | |
3 | +// License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 | +// file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 | + | |
6 | +#ifndef CIN_DSOUND_HPP | |
7 | +#define CIN_DSOUND_HPP | |
8 | +#pragma once | |
9 | + | |
10 | +/////////////////////////////////////////////////////////////////////////////// | |
11 | + | |
12 | +#ifndef WIN32_LEAN_AND_MEAN | |
13 | +#define WIN32_LEAN_AND_MEAN 1 | |
14 | +#endif | |
15 | + | |
16 | +#include <Windows.h> | |
17 | +#include <mmsystem.h> | |
18 | +#include <dsound.h> | |
19 | +#include <objbase.h> | |
20 | + | |
21 | +/////////////////////////////////////////////////////////////////////////////// | |
22 | + | |
23 | +struct Cin_Sound; | |
24 | +struct Cin_Loader; | |
25 | + | |
26 | +/////////////////////////////////////////////////////////////////////////////// | |
27 | + | |
28 | +struct Cin_Driver { | |
29 | +private: | |
30 | + IDirectSound8 *m_dsound; | |
31 | +public: | |
32 | + | |
33 | + Cin_Driver(); | |
34 | + ~Cin_Driver(); | |
35 | + | |
36 | + void createSound(Cin_Sound *out, const Cin_Loader &ld); | |
37 | + | |
38 | +}; | |
39 | + | |
40 | +/////////////////////////////////////////////////////////////////////////////// | |
41 | + | |
42 | +#endif // CIN_DSOUND_HPP |
@@ -0,0 +1,164 @@ | ||
1 | +// Copyright (c) 2018 AlaskanEmily | |
2 | +// This Source Code Form is subject to the terms of the Mozilla Public | |
3 | +// License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 | +// file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 | + | |
6 | +#include "cin_dsound_sound.hpp" | |
7 | +#include "cinnamon.h" | |
8 | +#include "cin_soft_loader.h" | |
9 | + | |
10 | +#include <mmreg.h> | |
11 | +#include <ks.h> | |
12 | +#include <ksmedia.h> | |
13 | + | |
14 | +/////////////////////////////////////////////////////////////////////////////// | |
15 | + | |
16 | +Cin_Sound::Cin_Sound(IDirectSound8 *dsound, const Cin_Loader &ld) | |
17 | + : m_dsound(dsound){ | |
18 | + | |
19 | + const unsigned sample_rate = ld.sample_rate; | |
20 | + const unsigned channels = ld.channels; | |
21 | + const enum Cin_Format format = ld.format; | |
22 | + | |
23 | + WAVEFORMATEXTENSIBLE fmt; | |
24 | +#ifndef NDEBUG | |
25 | + // Poison the uninitialized format | |
26 | + memset((void*)&fmt, 0xFF, sizeof(fmt)); | |
27 | +#endif | |
28 | + | |
29 | + // Set format tag | |
30 | + switch(format){ | |
31 | + case Cin_eFormatS8: | |
32 | + case Cin_eFormatS16: | |
33 | + case Cin_eFormatS32: | |
34 | + fmt.Format.cbSize = 0; | |
35 | + fmt.Format.wFormatTag = WAVE_FORMAT_PCM; | |
36 | + break; | |
37 | + case Cin_eFormatFloat32: | |
38 | + case Cin_eFormatFloat64: | |
39 | + case Cin_eFormatULaw8: | |
40 | + fmt.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; | |
41 | + fmt.Format.cbSize = sizeof(fmt) - sizeof(fmt.Format); | |
42 | + break; | |
43 | + } | |
44 | + | |
45 | + unsigned bytes_per_sample = 0; | |
46 | + switch(format){ | |
47 | + case Cin_eFormatS8: | |
48 | + bytes_per_sample = 1; | |
49 | + break; | |
50 | + case Cin_eFormatS16: | |
51 | + bytes_per_sample = 2; | |
52 | + break; | |
53 | + case Cin_eFormatS32: | |
54 | + bytes_per_sample = 4; | |
55 | + break; | |
56 | + case Cin_eFormatFloat32: | |
57 | + bytes_per_sample = 4; | |
58 | + fmt.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; | |
59 | + fmt.Samples.wValidBitsPerSample = 32; | |
60 | + break; | |
61 | + case Cin_eFormatFloat64: | |
62 | + bytes_per_sample = 8; | |
63 | + fmt.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; | |
64 | + fmt.Samples.wValidBitsPerSample = 64; | |
65 | + break; | |
66 | + case Cin_eFormatULaw8: | |
67 | + bytes_per_sample = 1; | |
68 | + fmt.SubFormat = KSDATAFORMAT_SUBTYPE_MULAW; | |
69 | + fmt.Samples.wValidBitsPerSample = 8; | |
70 | + break; | |
71 | + } | |
72 | + | |
73 | + // Set channels | |
74 | + fmt.Format.nChannels = channels; | |
75 | + if(fmt.Format.wFormatTag != WAVE_FORMAT_PCM){ | |
76 | + switch(channels){ | |
77 | + case 1: | |
78 | + fmt.dwChannelMask = KSAUDIO_SPEAKER_MONO; | |
79 | + break; | |
80 | + case 2: | |
81 | + fmt.dwChannelMask = KSAUDIO_SPEAKER_STEREO; | |
82 | + break; | |
83 | + case 4: | |
84 | + fmt.dwChannelMask = KSAUDIO_SPEAKER_QUAD; | |
85 | + break; | |
86 | + case 6: | |
87 | + fmt.dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND; | |
88 | + break; | |
89 | + default: | |
90 | + fmt.dwChannelMask = 0; | |
91 | + } | |
92 | + } | |
93 | + | |
94 | + // Set sample rate | |
95 | + fmt.Format.nSamplesPerSec = sample_rate; | |
96 | + | |
97 | + // Block align | |
98 | + const unsigned align = channels * bytes_per_sample; | |
99 | + const unsigned bytes_per_sec = align * sample_rate; | |
100 | + fmt.Format.nBlockAlign = align; | |
101 | + | |
102 | + fmt.Format.nAvgBytesPerSec = bytes_per_sec; | |
103 | + | |
104 | + // Set bits per sample | |
105 | + fmt.Format.wBitsPerSample = bytes_per_sample << 3; | |
106 | + | |
107 | + // Check the size of the loader data. | |
108 | + unsigned byte_size = 0; | |
109 | + for(const struct Cin_LoaderData *i = ld.first, *const end = ld.last; i != end; i = i->next){ | |
110 | + byte_size += i->len; | |
111 | + } | |
112 | + | |
113 | + DSBUFFERDESC descriptor; | |
114 | + descriptor.dwSize = sizeof(DSBUFFERDESC); | |
115 | + descriptor.dwFlags = DSBCAPS_GLOBALFOCUS; | |
116 | + descriptor.dwBufferBytes = byte_size; | |
117 | + descriptor.dwReserved = 0; | |
118 | + descriptor.lpwfxFormat = &fmt.Format; | |
119 | + descriptor.guid3DAlgorithm = DS3DALG_DEFAULT; | |
120 | + | |
121 | + void* buffer_data; | |
122 | + DWORD buffer_size; | |
123 | + | |
124 | + if(m_dsound->CreateSoundBuffer(&descriptor, &m_buffer, NULL) != DS_OK){ | |
125 | + m_dsound->Release(); | |
126 | + m_buffer = NULL; | |
127 | + } | |
128 | + else if(m_buffer->Lock(0, 0, &buffer_data, &buffer_size, 0, 0, DSBLOCK_ENTIREBUFFER) == DS_OK && | |
129 | + buffer_size == byte_size){ | |
130 | + | |
131 | + Cin_LoaderMemcpy(ld.first, 0, buffer_data, byte_size); | |
132 | + m_buffer->Unlock(buffer_data, byte_size, NULL, 0); | |
133 | + } | |
134 | + else{ | |
135 | + m_dsound->Release(); | |
136 | + m_buffer->Release(); | |
137 | + m_buffer = NULL; | |
138 | + } | |
139 | +} | |
140 | + | |
141 | +/////////////////////////////////////////////////////////////////////////////// | |
142 | + | |
143 | +Cin_Sound::~Cin_Sound(){ | |
144 | + if(m_buffer != NULL){ | |
145 | + m_buffer->Release(); | |
146 | + m_dsound->Release(); | |
147 | + } | |
148 | +} | |
149 | + | |
150 | +/////////////////////////////////////////////////////////////////////////////// | |
151 | + | |
152 | +void Cin_Sound::play(bool loop){ | |
153 | + if(m_buffer != NULL) | |
154 | + m_buffer->Play(0, 0, loop ? DSBPLAY_LOOPING : 0); | |
155 | +} | |
156 | + | |
157 | +/////////////////////////////////////////////////////////////////////////////// | |
158 | + | |
159 | +void Cin_Sound::stop(){ | |
160 | + if(m_buffer != NULL) | |
161 | + m_buffer->Stop(); | |
162 | +} | |
163 | + | |
164 | +/////////////////////////////////////////////////////////////////////////////// |
@@ -0,0 +1,43 @@ | ||
1 | +// Copyright (c) 2018 AlaskanEmily | |
2 | +// This Source Code Form is subject to the terms of the Mozilla Public | |
3 | +// License, v. 2.0. If a copy of the MPL was not distributed with this | |
4 | +// file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
5 | + | |
6 | +#ifndef CIN_DSOUND_SOUND_HPP | |
7 | +#define CIN_DSOUND_SOUND_HPP | |
8 | +#pragma once | |
9 | + | |
10 | +/////////////////////////////////////////////////////////////////////////////// | |
11 | + | |
12 | +#ifndef WIN32_LEAN_AND_MEAN | |
13 | +#define WIN32_LEAN_AND_MEAN 1 | |
14 | +#endif | |
15 | + | |
16 | +#include <Windows.h> | |
17 | +#include <mmsystem.h> | |
18 | +#include <dsound.h> | |
19 | +#include <objbase.h> | |
20 | + | |
21 | +/////////////////////////////////////////////////////////////////////////////// | |
22 | + | |
23 | +struct Cin_Loader; | |
24 | + | |
25 | +/////////////////////////////////////////////////////////////////////////////// | |
26 | + | |
27 | +struct Cin_Sound{ | |
28 | +private: | |
29 | + IDirectSoundBuffer *m_buffer; | |
30 | + IDirectSound8 *const m_dsound; | |
31 | + | |
32 | +public: | |
33 | + | |
34 | + Cin_Sound(IDirectSound8 *dsound, const Cin_Loader &ld); | |
35 | + ~Cin_Sound(); | |
36 | + | |
37 | + void play(bool loop); | |
38 | + void stop(); | |
39 | +}; | |
40 | + | |
41 | +/////////////////////////////////////////////////////////////////////////////// | |
42 | + | |
43 | +#endif // CIN_DSOUND_SOUND_HPP |
@@ -0,0 +1,19 @@ | ||
1 | +# Any copyright is dedicated to the Public Domain. | |
2 | +# http://creativecommons.org/publicdomain/zero/1.0/ | |
3 | + | |
4 | +all: cin_dsound.lib | |
5 | + | |
6 | +OBJECTS=cin_dsound.obj cin_dsound_driver.obj cin_dsound_sound.obj | |
7 | +HEADERS=cin_dsound_driver.hpp cin_dsound_sound.hpp ..\cinnamon.h ..\cin_export.h ..\common\cin_soft_loader.h | |
8 | + | |
9 | +cin_dsound.lib: $(OBJECTS) | |
10 | + lib $(LIBFLAGS) /OUT:cin_dsound.lib $(OBJECTS) | |
11 | + | |
12 | +cin_dsound.obj: cin_dsound.cpp $(HEADERS) | |
13 | + cl $(CLFLAGS) cin_dsound.cpp | |
14 | + | |
15 | +cin_dsound_driver.obj: cin_dsound_driver.cpp $(HEADERS) | |
16 | + cl $(CLFLAGS) cin_dsound_driver.cpp | |
17 | + | |
18 | +cin_dsound_sound.obj: cin_dsound_sound.cpp $(HEADERS) | |
19 | + cl $(CLFLAGS) cin_dsound_sound.cpp |
@@ -0,0 +1,40 @@ | ||
1 | +# Any copyright is dedicated to the Public Domain. | |
2 | +# http://creativecommons.org/publicdomain/zero/1.0/ | |
3 | + | |
4 | +LINK=link | |
5 | +LINKFLAGS=/NOLOGO /DLL /OUT:cinnamon.dll /PDB:cinnamon.pdb /DEF:cinnamon.def | |
6 | +CLFLAGS=/nologo /GF /EHsc /arch:SSE /Zi /c /WX /DCIN_INTERNAL /I$(MAKEDIR) /I$(MAKEDIR)\common /Os /W3 /DCIN_DLL=1 | |
7 | +LIBFLAGS=/NOLOGO | |
8 | + | |
9 | +all: dsound sine_test.exe aucat.exe | |
10 | + | |
11 | +common_lib: | |
12 | + cd common && $(MAKE) /nologo /fnmakefile CLFLAGS="$(CLFLAGS)" LIBFLAGS="$(LIBFLAGS)" | |
13 | + | |
14 | +dsound_lib: | |
15 | + cd dsound && $(MAKE) /nologo /fnmakefile CLFLAGS="$(CLFLAGS)" LIBFLAGS="$(LIBFLAGS)" | |
16 | + | |
17 | +openal_lib: | |
18 | + cd openal && $(MAKE) /nologo /fnmakefile CLFLAGS="$(CLFLAGS)" LIBFLAGS="$(LIBFLAGS)" | |
19 | + | |
20 | +# This is needed to hint the linker what machine to use. This is important on cross compilers, where the linker uses the host platform. | |
21 | +dummy.obj: | |
22 | + cl $(CLFLAGS) /c /TP cinnamon.h /Fodummy.obj | |
23 | + | |
24 | +dsound: dsound_lib common_lib dummy.obj cinnamon.def | |
25 | + $(LINK) /DEBUG dummy.obj dsound\cin_dsound.lib common\cin_common.lib dsound.lib dxguid.lib ole32.lib user32.lib $(LINKFLAGS) | |
26 | + | |
27 | +openal: openal_lib common_lib dummy.obj cinnamon.def | |
28 | + $(LINK) /DEBUG dummy.obj openal\cin_openal.lib lib\OpenAL32.lib $(LINKFLAGS) | |
29 | + | |
30 | +sine_test.obj: | |
31 | + cl $(CLFLAGS) /c sine_test.c | |
32 | + | |
33 | +sine_test.exe: dsound sine_test.obj | |
34 | + $(LINK) /DEBUG sine_test.obj cinnamon.lib /NOLOGO /OUT:sine_test.exe | |
35 | + | |
36 | +aucat.obj: | |
37 | + cl $(CLFLAGS) /c aucat.c | |
38 | + | |
39 | +aucat.exe: dsound aucat.obj | |
40 | + $(LINK) /DEBUG aucat.obj cinnamon.lib /NOLOGO /OUT:aucat.exe |
@@ -0,0 +1,367 @@ | ||
1 | +/* | |
2 | + * Copyright (c) 2018 AlaskanEmily | |
3 | + * This Source Code Form is subject to the terms of the Mozilla Public | |
4 | + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
5 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
6 | + */ | |
7 | + | |
8 | +#include "cinnamon.h" | |
9 | +#include "cin_openal.h" | |
10 | +#include <assert.h> | |
11 | + | |
12 | +/*****************************************************************************/ | |
13 | + | |
14 | +CIN_PRIVATE_PURE(ALuint) | |
15 | + Cin_CinFormatToOpenALFormat(enum Cin_Format f, unsigned num_channels){ | |
16 | + | |
17 | + switch(f){ | |
18 | + case Cin_eFormatS8: | |
19 | + if(num_channels == 1) | |
20 | + return AL_FORMAT_MONO8; | |
21 | + if(num_channels == 2) | |
22 | + return AL_FORMAT_STEREO8; | |
23 | + if(num_channels == 4) | |
24 | + return AL_FORMAT_QUAD8; | |
25 | + assert(0); | |
26 | + return 0; | |
27 | + case Cin_eFormatS16: | |
28 | + if(num_channels == 1) | |
29 | + return AL_FORMAT_MONO16; | |
30 | + if(num_channels == 2) | |
31 | + return AL_FORMAT_STEREO16; | |
32 | + if(num_channels == 4) | |
33 | + return AL_FORMAT_QUAD16; | |
34 | + assert(0); | |
35 | + return 0; | |
36 | + case Cin_eFormatS32: | |
37 | + assert(0); | |
38 | + return 0; | |
39 | + case Cin_eFormatFloat32: | |
40 | + if(num_channels == 1) | |
41 | + return AL_FORMAT_MONO_FLOAT32; | |
42 | + if(num_channels == 2) | |
43 | + return AL_FORMAT_STEREO_FLOAT32; | |
44 | + assert(0); | |
45 | + return 0; | |
46 | + case Cin_eFormatFloat64: | |
47 | + if(num_channels == 1) | |
48 | + return AL_FORMAT_MONO_DOUBLE_EXT; | |
49 | + if(num_channels == 2) | |
50 | + return AL_FORMAT_STEREO_DOUBLE_EXT; | |
51 | + assert(0); | |
52 | + return 0; | |
53 | + case Cin_eFormatULaw8: | |
54 | + if(num_channels == 1) | |
55 | + return AL_FORMAT_MONO_MULAW_EXT; | |
56 | + if(num_channels == 2) | |
57 | + return AL_FORMAT_STEREO_MULAW_EXT; | |
58 | + if(num_channels == 4) | |
59 | + return AL_FORMAT_QUAD_MULAW; | |
60 | + assert(0); | |
61 | + return 0; | |
62 | + } | |
63 | + assert(0); | |
64 | + return 0; | |
65 | +} | |
66 | + | |
67 | +/*****************************************************************************/ | |
68 | + | |
69 | +CIN_PRIVATE(int) Cin_CinExtension(const struct Cin_Driver *drv, int ext){ | |
70 | + if((drv->ext & 1) == 0){ | |
71 | + short s = 0; | |
72 | + alcMakeContextCurrent(drv->ctx); | |
73 | + if(alIsExtensionPresent("AL_EXT_float32")) | |
74 | + s |= (1 << CIN_FLOAT_SUPPORTED); | |
75 | + | |
76 | + if(alIsExtensionPresent("AL_EXT_double")) | |
77 | + s |= (1 << CIN_DOUBLE_SUPPORTED); | |
78 | + | |
79 | + if(alIsExtensionPresent("AL_EXT_MULAW") || | |
80 | + alIsExtensionPresent("AL_EXT_mulaw") || | |
81 | + alIsExtensionPresent("AL_EXT_ULAW") || | |
82 | + alIsExtensionPresent("AL_EXT_ulaw") ) | |
83 | + s |= (1 << CIN_MULAW_SUPPORTED); | |
84 | + | |
85 | + if(alIsExtensionPresent("AL_EXT_MCFORMATS") || | |
86 | + alIsExtensionPresent("AL_EXT_mcformats")) | |
87 | + s |= (1 << CIN_QUAD_SUPPORTED); | |
88 | + | |
89 | + if(alIsExtensionPresent("AL_EXT_MULAW_MCFORMATS") || | |
90 | + alIsExtensionPresent("AL_EXT_mulaw_mcformats") || | |
91 | + alIsExtensionPresent("AL_EXT_ULAW_MCFORMATS") || | |
92 | + alIsExtensionPresent("AL_EXT_ulaw_mcformats")) | |
93 | + s |= (1 << CIN_MULAW_QUAD_SUPPORTED); | |
94 | + | |
95 | + ((struct Cin_Driver *)drv)->ext = s; | |
96 | + | |
97 | + return s & (1 << ext); | |
98 | + } | |
99 | + | |
100 | + return drv->ext & (1 << ext); | |
101 | +} | |
102 | + | |
103 | +/*****************************************************************************/ | |
104 | + | |
105 | +CIN_PRIVATE(int) Cin_CleanOpenALSound(ALuint snd, ALuint *out){ | |
106 | + ALint i; | |
107 | + alGetSourcei(snd, AL_BUFFERS_PROCESSED, &i); | |
108 | + if(i > 0){ | |
109 | + ALuint buffers[16]; | |
110 | + if(out != NULL){ | |
111 | + --i; | |
112 | + alSourceUnqueueBuffers(snd, 1, out); | |
113 | + } | |
114 | + do{ | |
115 | + const unsigned to_delete = (i >= 16) ? 16 : i; | |
116 | + alSourceUnqueueBuffers(snd, to_delete, buffers); | |
117 | + alDeleteBuffers(to_delete, buffers); | |
118 | + i -= to_delete; | |
119 | + }while(i > 0); | |
120 | + return 1; | |
121 | + } /* if(i > 0) */ | |
122 | + return 0; | |
123 | +} | |
124 | + | |
125 | +/*****************************************************************************/ | |
126 | + | |
127 | +unsigned Cin_StructDriverSize(){ | |
128 | + return sizeof(struct Cin_Driver); | |
129 | +} | |
130 | + | |
131 | +/*****************************************************************************/ | |
132 | + | |
133 | +enum Cin_DriverError Cin_CreateDriver(struct Cin_Driver *drv){ | |
134 | + ALCcontext *ctx; | |
135 | + ALCdevice *dev; | |
136 | + | |
137 | + assert(drv != NULL); | |
138 | + | |
139 | + dev = alcOpenDevice(NULL); | |
140 | + if(dev == NULL){ | |
141 | + return Cin_eDriverNoDevice; | |
142 | + } | |
143 | + | |
144 | + ctx = alcCreateContext(dev, NULL); | |
145 | + | |
146 | + if(ctx == NULL){ | |
147 | + alcCloseDevice(dev); | |
148 | + return Cin_eDriverFailure; | |
149 | + } | |
150 | + | |
151 | + drv->ctx = ctx; | |
152 | + drv->dev = dev; | |
153 | + drv->ext = 0; | |
154 | + | |
155 | + return Cin_eDriverSuccess; | |
156 | +} | |
157 | + | |
158 | +/*****************************************************************************/ | |
159 | + | |
160 | +void Cin_DestroyDriver(struct Cin_Driver *drv){ | |
161 | + assert(drv != NULL); | |
162 | + assert(drv->ctx != NULL); | |
163 | + assert(drv->dev != NULL); | |
164 | + | |
165 | + alcDestroyContext(drv->ctx); | |
166 | + alcCloseDevice(drv->dev); | |
167 | +} | |
168 | + | |
169 | +/*****************************************************************************/ | |
170 | + | |
171 | +enum Cin_DriverError Cin_DriverSupportsFormat( | |
172 | + const struct Cin_Driver *drv, | |
173 | + enum Cin_Format format, | |
174 | + unsigned num_channels){ | |
175 | + | |
176 | + assert(drv != NULL); | |
177 | + assert(drv->ctx != NULL); | |
178 | + assert(drv->dev != NULL); | |
179 | + | |
180 | + switch(format){ | |
181 | + case Cin_eFormatS16: /* FALLTHROUGH */ | |
182 | + case Cin_eFormatS8: | |
183 | + if(num_channels == 1 || num_channels == 2){ | |
184 | + return Cin_eDriverSuccess; | |
185 | + } | |
186 | + else if(num_channels == 4){ | |
187 | + return Cin_CinExtension(drv, CIN_QUAD_SUPPORTED) ? | |
188 | + Cin_eDriverSuccess : Cin_eDriverUnsupportedNumChannels; | |
189 | + } | |
190 | + else{ | |
191 | + return Cin_eDriverUnsupportedNumChannels; | |
192 | + } | |
193 | + case Cin_eFormatS32: | |
194 | + return Cin_eDriverUnsupportedFormat; | |
195 | + case Cin_eFormatFloat32: | |
196 | + if(num_channels == 1 || num_channels == 2){ | |
197 | + return Cin_CinExtension(drv, CIN_FLOAT_SUPPORTED) ? | |
198 | + Cin_eDriverSuccess : Cin_eDriverUnsupportedFormat; | |
199 | + } | |
200 | + else{ | |
201 | + return Cin_eDriverUnsupportedNumChannels; | |
202 | + } | |
203 | + case Cin_eFormatFloat64: | |
204 | + if(num_channels == 1 || num_channels == 2){ | |
205 | + return Cin_CinExtension(drv, CIN_DOUBLE_SUPPORTED) ? | |
206 | + Cin_eDriverSuccess : Cin_eDriverUnsupportedFormat; | |
207 | + } | |
208 | + else{ | |
209 | + return Cin_eDriverUnsupportedNumChannels; | |
210 | + } | |
211 | + case Cin_eFormatULaw8: | |
212 | + if(!Cin_CinExtension(drv, CIN_MULAW_SUPPORTED)) | |
213 | + return Cin_eDriverUnsupportedFormat; | |
214 | + if(num_channels == 1 || num_channels == 2){ | |
215 | + return Cin_eDriverSuccess; | |
216 | + } | |
217 | + else if(num_channels == 4){ | |
218 | + return Cin_CinExtension(drv, CIN_MULAW_QUAD_SUPPORTED) ? | |
219 | + Cin_eDriverSuccess : Cin_eDriverUnsupportedNumChannels; | |
220 | + } | |
221 | + else{ | |
222 | + return Cin_eDriverUnsupportedNumChannels; | |
223 | + } | |
224 | + case Cin_eFormatNUM_FORMATS: | |
225 | + return Cin_eDriverInvalidFormat; | |
226 | + } | |
227 | + | |
228 | + return Cin_eDriverInvalidFormat; | |
229 | +} | |
230 | + | |
231 | +/*****************************************************************************/ | |
232 | + | |
233 | +enum Cin_DriverError Cin_DriverSupportsSampleRate( | |
234 | + const struct Cin_Driver *drv, | |
235 | + unsigned rate){ | |
236 | + | |
237 | + assert(drv != NULL); | |
238 | + assert(drv->ctx != NULL); | |
239 | + assert(drv->dev != NULL); | |
240 | + | |
241 | + switch(rate){ | |
242 | + case 8000: /* FALLTHROUGH */ | |
243 | + case 11025: /* FALLTHROUGH */ | |
244 | + case 16000: /* FALLTHROUGH */ | |
245 | + case 22050: /* FALLTHROUGH */ | |
246 | + case 44100: /* FALLTHROUGH */ | |
247 | + case 48000: | |
248 | + return Cin_eDriverSuccess; | |
249 | + default: | |
250 | + return Cin_eDriverUnsupportedSampleRate; | |
251 | + | |
252 | + } | |
253 | +} | |
254 | + | |
255 | +/*****************************************************************************/ | |
256 | + | |
257 | +unsigned Cin_StructLoaderSize(){ | |
258 | + return sizeof(struct Cin_Loader); | |
259 | +} | |
260 | + | |
261 | +/*****************************************************************************/ | |
262 | + | |
263 | +enum Cin_LoaderError Cin_CreateLoader(struct Cin_Loader *out, | |
264 | + struct Cin_Driver *drv, | |
265 | + unsigned sample_rate, | |
266 | + unsigned num_channels, | |
267 | + enum Cin_Format format){ | |
268 | + | |
269 | + assert(out); | |
270 | + assert(drv); | |
271 | + assert(drv->ctx); | |
272 | + assert(drv->dev); | |
273 | + | |
274 | + { | |
275 | + const enum Cin_LoaderError err = | |
276 | + Cin_FormatCompatible(drv, sample_rate, num_channels, format); | |
277 | + if(err != Cin_eLoaderSuccess) | |
278 | + return err; | |
279 | + } | |
280 | + | |
281 | + alcMakeContextCurrent(drv->ctx); | |
282 | + | |
283 | + out->snd.ctx = drv->ctx; | |
284 | + | |
285 | + { | |
286 | + ALuint source; | |
287 | + alGenSources(1, &source); | |
288 | + out->snd.snd = source; | |
289 | + | |
290 | + alSourcef(source, AL_PITCH, 1.0f); | |
291 | + alSourcef(source, AL_GAIN, 1.0f); | |
292 | + alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f); | |
293 | + alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); | |
294 | + alSourcei(source, AL_LOOPING, AL_FALSE); | |
295 | + } | |
296 | + | |
297 | + out->format = Cin_CinFormatToOpenALFormat(format, num_channels); | |
298 | + out->channels = num_channels; | |
299 | + out->sample_rate = sample_rate; | |
300 | + | |
301 | + return Cin_eLoaderSuccess; | |
302 | +} | |
303 | + | |
304 | +/*****************************************************************************/ | |
305 | + | |
306 | +enum Cin_LoaderError Cin_LoaderPut(struct Cin_Loader *ld, | |
307 | + const void *data, | |
308 | + unsigned byte_size){ | |
309 | + | |
310 | + ALuint buffer; | |
311 | + alcMakeContextCurrent(ld->snd.ctx); | |
312 | + if(!Cin_CleanOpenALSound(ld->snd.snd, &buffer)){ | |
313 | + alGenBuffers(1, &buffer); | |
314 | + } | |
315 | + alBufferData(buffer, ld->format, data, byte_size, ld->sample_rate); | |
316 | + alSourceQueueBuffers(ld->snd.snd, 1, &buffer); | |
317 | + | |
318 | + return Cin_eLoaderSuccess; | |
319 | +} | |
320 | + | |
321 | +/*****************************************************************************/ | |
322 | + | |
323 | +enum Cin_LoaderError Cin_LoaderFinalize(struct Cin_Loader *ld, | |
324 | + struct Cin_Sound *out){ | |
325 | + | |
326 | + ALuint buffer; | |
327 | + alcMakeContextCurrent(ld->snd.ctx); | |
328 | + if(Cin_CleanOpenALSound(ld->snd.snd, &buffer)){ | |
329 | + alDeleteBuffers(1, &buffer); | |
330 | + } | |
331 | + | |
332 | + out->snd = ld->snd.snd; | |
333 | + out->ctx = ld->snd.ctx; | |
334 | + | |
335 | + ld->snd.snd = 0; | |
336 | + return Cin_eLoaderSuccess; | |
337 | +} | |
338 | + | |
339 | +/*****************************************************************************/ | |
340 | + | |
341 | +unsigned Cin_StructSoundSize(){ | |
342 | + return sizeof(struct Cin_Sound); | |
343 | +} | |
344 | + | |
345 | +/*****************************************************************************/ | |
346 | + | |
347 | +enum Cin_SoundError Cin_SoundPlay(struct Cin_Sound *snd){ | |
348 | + alcMakeContextCurrent(snd->ctx); | |
349 | + alSourcePlay(snd->snd); | |
350 | + return Cin_eSoundSuccess; | |
351 | +} | |
352 | + | |
353 | +/*****************************************************************************/ | |
354 | + | |
355 | +enum Cin_SoundError Cin_SoundStop(struct Cin_Sound *snd){ | |
356 | + alcMakeContextCurrent(snd->ctx); | |
357 | + alSourceStop(snd->snd); | |
358 | + alSourceRewind(snd->snd); | |
359 | + return Cin_eSoundSuccess; | |
360 | +} | |
361 | + | |
362 | +/*****************************************************************************/ | |
363 | + | |
364 | +void Cin_DestroySound(struct Cin_Sound *snd){ | |
365 | + alcMakeContextCurrent(snd->ctx); | |
366 | + alDeleteSources(1, &(snd->snd)); | |
367 | +} |
@@ -0,0 +1,120 @@ | ||
1 | +/* Copyright (c) 2018 AlaskanEmily | |
2 | + * | |
3 | + * This Source Code Form is subject to the terms of the Mozilla Public | |
4 | + * License, v. 2.0. If a copy of the MPL was not distributed with this | |
5 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |
6 | + */ | |
7 | + | |
8 | +#ifndef CIN_OPENAL_H | |
9 | +#define CIN_OPENAL_H | |
10 | +#pragma once | |
11 | + | |
12 | +/* TODO: Apple's OpenAL apparently has a hard cap of 32 on the number of | |
13 | + * buffers. We should probably check for this, and use the softloader in that | |
14 | + * case instead. | |
15 | + */ | |
16 | + | |
17 | +#ifdef __APPLE__ | |
18 | + #include <OpenAL/al.h> | |
19 | + #include <OpenAL/alc.h> | |
20 | +#else | |
21 | + #include <AL/al.h> | |
22 | + #include <AL/alc.h> | |
23 | +#endif | |
24 | + | |
25 | +#include "cinnamon.h" | |
26 | + | |
27 | +#ifdef __cplusplus | |
28 | +extern "C" { | |
29 | +#endif | |
30 | + | |
31 | +#ifndef AL_FORMAT_MONO_FLOAT32 | |
32 | +#define AL_FORMAT_MONO_FLOAT32 0x10010 | |
33 | +#endif | |
34 | + | |
35 | +#ifndef AL_FORMAT_STEREO_FLOAT32 | |
36 | +#define AL_FORMAT_STEREO_FLOAT32 0x10011 | |
37 | +#endif | |
38 | + | |
39 | +#ifndef AL_FORMAT_MONO_DOUBLE_EXT | |
40 | +#define AL_FORMAT_MONO_DOUBLE_EXT 0x10012 | |
41 | +#endif | |
42 | + | |
43 | +#ifndef AL_FORMAT_STEREO_DOUBLE_EXT | |
44 | +#define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013 | |
45 | +#endif | |
46 | + | |
47 | +#ifndef AL_FORMAT_MONO_MULAW_EXT | |
48 | +#define AL_FORMAT_MONO_MULAW_EXT 0x10014 | |
49 | +#endif | |
50 | + | |
51 | +#ifndef AL_FORMAT_STEREO_MULAW_EXT | |
52 | +#define AL_FORMAT_STEREO_MULAW_EXT 0x10015 | |
53 | +#endif | |
54 | + | |
55 | +#ifndef AL_FORMAT_QUAD_MULAW | |
56 | +#define AL_FORMAT_QUAD_MULAW 0x10021 | |
57 | +#endif | |
58 | + | |
59 | +#ifndef AL_FORMAT_QUAD_MULAW_EXT | |
60 | +#define AL_FORMAT_QUAD_MULAW_EXT AL_FORMAT_QUAD_MULAW | |
61 | +#endif | |
62 | + | |
63 | +#ifndef AL_FORMAT_QUAD8 | |
64 | +#define AL_FORMAT_QUAD8 0x1204 | |
65 | +#endif | |
66 | + | |
67 | +#ifndef AL_FORMAT_QUAD16 | |
68 | +#define AL_FORMAT_QUAD16 0x1205 | |
69 | +#endif | |
70 | + | |
71 | +/*****************************************************************************/ | |
72 | + | |
73 | +CIN_PRIVATE_PURE(ALuint) | |
74 | + Cin_CinFormatToOpenALFormat(enum Cin_Format f, unsigned num_channels); | |
75 | + | |
76 | +/*****************************************************************************/ | |
77 | + | |
78 | +CIN_PRIVATE(int) Cin_CinExtension(const struct Cin_Driver *drv, int ext); | |
79 | + | |
80 | +/*****************************************************************************/ | |
81 | + | |
82 | +CIN_PRIVATE(int) Cin_CleanOpenALSound(ALuint snd, ALuint *out); | |
83 | + | |
84 | +/*****************************************************************************/ | |
85 | + | |
86 | +/* Defines the bits in exts that indicate presence. */ | |
87 | +#define CIN_FLOAT_SUPPORTED 1 | |
88 | +#define CIN_DOUBLE_SUPPORTED 2 | |
89 | +#define CIN_MULAW_SUPPORTED 3 | |
90 | +#define CIN_MULAW_QUAD_SUPPORTED 4 | |
91 | +#define CIN_QUAD_SUPPORTED 5 | |
92 | + | |
93 | +/*****************************************************************************/ | |
94 | + | |
95 | +struct Cin_Driver{ | |
96 | + ALCcontext *ctx; | |
97 | + ALCdevice *dev; | |
98 | + /* See the CIN_*_SUPPORTED macros. */ | |
99 | + short ext; | |
100 | +}; | |
101 | + | |
102 | +/*****************************************************************************/ | |
103 | + | |
104 | +struct Cin_Sound { | |
105 | + ALCcontext *ctx; | |
106 | + ALuint snd; | |
107 | +}; | |
108 | + | |
109 | +/*****************************************************************************/ | |
110 | + | |
111 | +struct Cin_Loader { | |
112 | + struct Cin_Sound snd; | |
113 | + ALuint format, sample_rate, channels; | |
114 | +}; | |
115 | + | |
116 | +#ifdef __cplusplus | |
117 | +} // extern "C" | |
118 | +#endif | |
119 | + | |
120 | +#endif /* CIN_OPENAL_H */ |
@@ -0,0 +1,153 @@ | ||
1 | +/* | |
2 | + * Any copyright is dedicated to the Public Domain. | |
3 | + * http://creativecommons.org/publicdomain/zero/1.0/ | |
4 | + */ | |
5 | + | |
6 | +#define _POSIX_C_SOURCE 199309L | |
7 | + | |
8 | +#include "cin_driver.h" | |
9 | +#include "cin_loader.h" | |
10 | +#include "cin_sound.h" | |
11 | + | |
12 | +#include <assert.h> | |
13 | +#include <stdlib.h> | |
14 | +#include <stdio.h> | |
15 | +#include <math.h> | |
16 | + | |
17 | +/* Get a sleep function. */ | |
18 | +#ifdef _WIN32 | |
19 | +#define WIN32_LEAN_AND_MEAN | |
20 | +#include <Windows.h> | |
21 | +#else | |
22 | +#include <time.h> | |
23 | +#include <unistd.h> | |
24 | +#endif | |
25 | + | |
26 | +#ifndef M_PI | |
27 | +#define M_PI 3.14159265f | |
28 | +#endif | |
29 | + | |
30 | +int main(int argc, char **argv){ | |
31 | + struct Cin_Driver *const driver = malloc(Cin_StructDriverSize()); | |
32 | + if(driver == NULL){ | |
33 | + puts("Could not allocate driver."); | |
34 | + return EXIT_FAILURE; | |
35 | + } | |
36 | + | |
37 | + (void)argc; | |
38 | + (void)argv; | |
39 | + | |
40 | + assert(Cin_StructDriverSize() > 0); | |
41 | + assert(Cin_StructLoaderSize() > 0); | |
42 | + assert(Cin_StructSoundSize() > 0); | |
43 | + | |
44 | + if(driver == NULL || Cin_CreateDriver(driver) == Cin_eDriverSuccess){ | |
45 | + struct Cin_Loader *const loader = malloc(Cin_StructLoaderSize()); | |
46 | + struct Cin_Sound *const sound = malloc(Cin_StructSoundSize()); | |
47 | + | |
48 | + /* TODO: Make these configurable */ | |
49 | + unsigned num_channels = 2, | |
50 | + sample_rate = 48000, | |
51 | + sin_frequency = 440, | |
52 | + num_seconds = 11; | |
53 | + | |
54 | + /* Times two for the size of int16_t */ | |
55 | + const unsigned data_size = sample_rate * num_channels << 1; | |
56 | + short *const data = malloc(data_size); | |
57 | + | |
58 | + if(!(loader != NULL && sound != NULL && data != NULL)){ | |
59 | + if(!loader) | |
60 | + puts("Could not allocate loader."); | |
61 | + if(!sound) | |
62 | + puts("Could not allocate sound."); | |
63 | + if(!data) | |
64 | + puts("Could not allocate buffer for generating audio."); | |
65 | + free(loader); | |
66 | + free(sound); | |
67 | + free(data); | |
68 | + Cin_DestroyDriver(driver); | |
69 | + free(driver); | |
70 | + return EXIT_FAILURE; | |
71 | + } | |
72 | + | |
73 | + { | |
74 | + const enum Cin_LoaderError err = | |
75 | + Cin_CreateLoader(loader, | |
76 | + driver, | |
77 | + sample_rate, | |
78 | + num_channels, | |
79 | + Cin_eFormatS16); | |
80 | + if(err != Cin_eLoaderSuccess){ | |
81 | + free(loader); | |
82 | + free(sound); | |
83 | + free(data); | |
84 | + Cin_DestroyDriver(driver); | |
85 | + free(driver); | |
86 | + puts("Could not create loader."); | |
87 | + return EXIT_FAILURE; | |
88 | + } | |
89 | + } | |
90 | + | |
91 | + { | |
92 | + const float multiplier = M_PI * 2 * (float)sin_frequency; | |
93 | + unsigned i; | |
94 | + /* Create one second of 44100 Hz audio */ | |
95 | + /* We want to cycle sequence 440 times a second. */ | |
96 | + for(i = 0; i < sample_rate; i++){ | |
97 | + const float sequence = | |
98 | + multiplier * (float)i / (float)sample_rate; | |
99 | + const float sample = sinf(sequence) * (float)(0xFFFF >> 1); | |
100 | + const unsigned at = i * num_channels; | |
101 | + data[at] = data[at + 1] = (short)(sample * 0.1); | |
102 | + } | |
103 | + | |
104 | + /* Put the second once for each second of data we want to play. */ | |
105 | + for(i = 0; i < num_seconds; i++){ | |
106 | + Cin_LoaderPut(loader, data, data_size); | |
107 | + } | |
108 | + } | |
109 | + | |
110 | + /* Create a new sound from this loader. */ | |
111 | + { | |
112 | + const enum Cin_LoaderError err = | |
113 | + Cin_LoaderFinalize(loader, sound); | |
114 | + | |
115 | + free(loader); | |
116 | + free(data); | |
117 | + | |
118 | + if(err != Cin_eLoaderSuccess){ | |
119 | + free(sound); | |
120 | + Cin_DestroyDriver(driver); | |
121 | + free(driver); | |
122 | + puts("Could not create sound."); | |
123 | + return EXIT_FAILURE; | |
124 | + } | |
125 | + } | |
126 | + | |
127 | + puts("Playing"); | |
128 | + Cin_SoundPlay(sound); | |
129 | + | |
130 | +#ifdef _WIN32 | |
131 | + Sleep((1000 * num_seconds) + 1); | |
132 | +#else | |
133 | + { | |
134 | + struct timespec t; | |
135 | + t.tv_sec = num_seconds; | |
136 | + t.tv_nsec = 1; | |
137 | + nanosleep(&t, NULL); | |
138 | + } | |
139 | +#endif | |
140 | + | |
141 | + Cin_SoundStop(sound); | |
142 | + Cin_DestroySound(sound); | |
143 | + free(sound); | |
144 | + } | |
145 | + else{ | |
146 | + puts("Could not create driver."); | |
147 | + return EXIT_FAILURE; | |
148 | + } | |
149 | + | |
150 | + Cin_DestroyDriver(driver); | |
151 | + free(driver); | |
152 | + return EXIT_SUCCESS; | |
153 | +} |