├── Makefile ├── README.md ├── beep.c └── beep.h /Makefile: -------------------------------------------------------------------------------- 1 | OS:=$(shell uname) 2 | ifeq ($(OS),Darwin) 3 | LDFLAGS=-framework AudioUnit 4 | endif 5 | 6 | ifeq ($(OS),Linux) 7 | LDFLAGS=-lasound 8 | endif 9 | 10 | beep: beep.o 11 | $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) 12 | 13 | clean: 14 | rm -f beep beep.exe beep.o 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beep 2 | 3 | Beep is a minimal cross-platform sound playback library. All it does is beeping at the given frequency for the given duration of time. 4 | 5 | * Good old C89 code with globals. 6 | * Very small, ~100 lines of code for all platforms. 7 | * Easy to use, API is a single blocking function. 8 | * Uses ALSA on Linux, CoreAudio on macOS and Beep() WINAPI function on Windows. 9 | 10 | The API is the following: 11 | 12 | ```c 13 | int beep(int frequency, int duration); 14 | ``` 15 | 16 | Frequency is in Hz, duration is in milliseconds. To convert note index into a frequency use the following formula: `440 * pow(2, ((note - 49) / 12))`. 17 | 18 | There is a also a `beep` utility which shows how the API can be used. 19 | 20 | If you found an issue or would like to propose a feature - PRs are welcome as long as they are small and simple! 21 | 22 | Code is distributed under MIT license, feel free to use in commercial projects. 23 | -------------------------------------------------------------------------------- /beep.c: -------------------------------------------------------------------------------- 1 | #include "beep.h" 2 | 3 | #include 4 | 5 | int main(int argc, char *argv[]) { 6 | for (int i = 0; i < (argc - 1) / 2; i++) { 7 | beep(atoi(argv[i * 2 + 1]), atoi(argv[i * 2 + 2])); 8 | } 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /beep.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2020 Serge Zaitsev 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | #ifndef BEEP_H 25 | #define BEEP_H 26 | 27 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) 28 | /* On Windows use the built-in Beep() function from */ 29 | int beep(int freq, int ms) { return Beep(freq, ms); } 30 | #elif __linux__ 31 | /* On Linux use alsa in synchronous mode, open "default" device in signed 8-bit 32 | * mode at 8kHz, mono, request for 20ms latency. Device is opened on first call 33 | * and never closed. */ 34 | #include 35 | int beep(int freq, int ms) { 36 | static void *pcm = NULL; 37 | if (pcm == NULL) { 38 | if (snd_pcm_open(&pcm, "default", 0, 0)) { 39 | return -1; 40 | } 41 | snd_pcm_set_params(pcm, 1, 3, 1, 8000, 1, 20000); 42 | } 43 | unsigned char buf[2400]; 44 | long frames; 45 | long phase; 46 | for (int i = 0; i < ms / 50; i++) { 47 | snd_pcm_prepare(pcm); 48 | for (int j = 0; j < sizeof(buf); j++) { 49 | buf[j] = freq > 0 ? (255 * j * freq / 8000) : 0; 50 | } 51 | int r = snd_pcm_writei(pcm, buf, sizeof(buf)); 52 | if (r < 0) { 53 | snd_pcm_recover(pcm, r, 0); 54 | } 55 | } 56 | return 0; 57 | } 58 | #elif __APPLE__ 59 | #include 60 | 61 | static dispatch_semaphore_t stopped, playing, done; 62 | 63 | static int beep_freq; 64 | static int beep_samples; 65 | static int counter = 0; 66 | 67 | static int initialized = 0; 68 | static unsigned char theta = 0; 69 | 70 | static OSStatus tone_cb(void *inRefCon, 71 | AudioUnitRenderActionFlags *ioActionFlags, 72 | const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, 73 | UInt32 inNumberFrames, AudioBufferList *ioData) { 74 | unsigned int frame; 75 | unsigned char *buf = ioData->mBuffers[0].mData; 76 | unsigned long i = 0; 77 | 78 | for (i = 0; i < inNumberFrames; i++) { 79 | while (counter == 0) { 80 | dispatch_semaphore_wait(playing, DISPATCH_TIME_FOREVER); 81 | counter = beep_samples; 82 | } 83 | buf[i] = beep_freq > 0 ? (255 * theta * beep_freq / 8000) : 0; 84 | theta++; 85 | counter--; 86 | if (counter == 0) { 87 | dispatch_semaphore_signal(done); 88 | dispatch_semaphore_signal(stopped); 89 | } 90 | } 91 | return 0; 92 | } 93 | 94 | int beep(int freq, int ms) { 95 | if (!initialized) { 96 | AudioComponent output; 97 | AudioUnit unit; 98 | AudioComponentDescription descr; 99 | AURenderCallbackStruct cb; 100 | AudioStreamBasicDescription stream; 101 | 102 | initialized = 1; 103 | 104 | stopped = dispatch_semaphore_create(1); 105 | playing = dispatch_semaphore_create(0); 106 | done = dispatch_semaphore_create(0); 107 | 108 | descr.componentType = kAudioUnitType_Output, 109 | descr.componentSubType = kAudioUnitSubType_DefaultOutput, 110 | descr.componentManufacturer = kAudioUnitManufacturer_Apple, 111 | 112 | cb.inputProc = tone_cb; 113 | 114 | stream.mFormatID = kAudioFormatLinearPCM; 115 | stream.mFormatFlags = 0; 116 | stream.mSampleRate = 8000; 117 | stream.mBitsPerChannel = 8; 118 | stream.mChannelsPerFrame = 1; 119 | stream.mFramesPerPacket = 1; 120 | stream.mBytesPerFrame = 1; 121 | stream.mBytesPerPacket = 1; 122 | 123 | output = AudioComponentFindNext(NULL, &descr); 124 | AudioComponentInstanceNew(output, &unit); 125 | AudioUnitSetProperty(unit, kAudioUnitProperty_SetRenderCallback, 126 | kAudioUnitScope_Input, 0, &cb, sizeof(cb)); 127 | AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, 128 | kAudioUnitScope_Input, 0, &stream, sizeof(stream)); 129 | AudioUnitInitialize(unit); 130 | AudioOutputUnitStart(unit); 131 | } 132 | 133 | dispatch_semaphore_wait(stopped, DISPATCH_TIME_FOREVER); 134 | beep_freq = freq; 135 | beep_samples = ms * 8; 136 | dispatch_semaphore_signal(playing); 137 | dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER); 138 | return 0; 139 | } 140 | #else 141 | #error "unknown platform" 142 | #endif 143 | 144 | #endif /* BEEP_H */ 145 | --------------------------------------------------------------------------------