• R/O
  • HTTP
  • SSH
  • HTTPS

cinnamon: Commit

Cinnamon audio library


Commit MetaInfo

Revisionbf3ed9a4403b7797fd6426f3946fa2ae3c6c65b9 (tree)
Zeit2019-01-22 10:05:52
AutorAlaskanEmily <emily@alas...>
CommiterAlaskanEmily

Log Message

Major refactor of source location. Remove unused DSP components for now. Change all DirectSound sounds to use full buffering.

Ändern Zusammenfassung

Diff

--- /dev/null
+++ b/src/aucat.c
@@ -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+}
--- /dev/null
+++ b/src/cin_export.h
@@ -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 */
--- /dev/null
+++ b/src/cinnamon.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 */
--- /dev/null
+++ b/src/common/cin_soft_loader.c
@@ -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+}
--- /dev/null
+++ b/src/common/cin_soft_loader.h
@@ -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 */
--- /dev/null
+++ b/src/common/nmakefile
@@ -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
--- /dev/null
+++ b/src/dsound/cin_dsound.cpp
@@ -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+}
--- /dev/null
+++ b/src/dsound/cin_dsound_driver.cpp
@@ -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
--- /dev/null
+++ b/src/dsound/cin_dsound_driver.hpp
@@ -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
--- /dev/null
+++ b/src/dsound/cin_dsound_sound.cpp
@@ -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+///////////////////////////////////////////////////////////////////////////////
--- /dev/null
+++ b/src/dsound/cin_dsound_sound.hpp
@@ -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
--- /dev/null
+++ b/src/dsound/nmakefile
@@ -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
--- /dev/null
+++ b/src/nmakefile
@@ -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
--- /dev/null
+++ b/src/openal/cin_openal.c
@@ -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+}
--- /dev/null
+++ b/src/openal/cin_openal.h
@@ -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 */
--- /dev/null
+++ b/src/sine_test.c
@@ -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+}
Show on old repository browser