1 /* Copyright (c) 2019-2021 AlaskanEmily, Transnat Games
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/.
10 #include "cin_oss_driver.h"
11 #include "cin_mixer_sound.h"
14 #define _DEFAULT_SOURCE
24 /*****************************************************************************/
26 void *Cin_OSS_ThreadFunc(void *v){
27 struct Cin_MixerSound *channels[CIN_OSS_SOUND_CHANNELS];
28 unsigned active_channels = 0, i;
29 struct Cin_Driver *const drv = v;
32 const Cin_MixerSound_PtrPtr drv_channels = Cin_OSS_GetChannels(drv);
33 const unsigned rate = Cin_OSS_GetSampleRate(drv);
34 const int dev = Cin_OSS_GetDevice(drv);
35 const int num_channels = Cin_OSS_GetNumChannels(drv);
36 const int format = Cin_OSS_GetFormat(drv);
37 const int bytes_per_frame = CIN_FORMAT_BYTES_PER_SAMPLE(format) * num_channels;
38 const int buffer_size = (bytes_per_frame * rate) / 10;
40 const void *input_data[CIN_OSS_SOUND_CHANNELS+1];
42 void *const buffer = malloc(buffer_size);
44 CIN_DSP_NEW_STACK_FMT_STRUCT(dsp_fmt);
46 CIN_DSP_MIXER_FORMAT_SET(dsp_fmt,
53 clock_gettime(CLOCK_REALTIME, &tm);
55 if(Cin_OSS_ProcessCommands(drv) == 1){
57 CIN_DSP_FREE_STACK_FMT_STRUCT(dsp_fmt);
62 for(i = 0; i < CIN_OSS_SOUND_CHANNELS; i++){
63 struct Cin_MixerSound *const snd = drv_channels[i];
65 channels[active_channels++] = snd;
67 /* Clear the sound if this will be its final play. */
68 if(snd->position + buffer_size >= snd->byte_len){
69 drv_channels[i] = NULL;
74 if(active_channels != 0){
75 /* First, mix any finalizing channels (channels without enough data to
80 /* Find the first channel which is being finalized, if any. */
82 if(channels[i]->position + buffer_size >
83 channels[i]->byte_len){
86 }while(++i < active_channels);
88 /* Check this only if we broke early (found a finalizing channel) */
89 if(i != active_channels){
90 input_data[0] = buffer;
92 /* Very fortunately, zero-init is ALSO zero in float and double */
93 memset(buffer, 0, buffer_size);
96 struct Cin_MixerSound *const snd = channels[i];
97 const int remaining_length =
98 snd->byte_len - snd->position;
99 if(remaining_length < buffer_size){
100 input_data[1] = CIN_MIXER_SOUND_PCM(channels[i]) +
103 Cin_DSP_Mix(remaining_length,
109 /* Pull out this channel by simply swapping in the final
110 * element. This makes removing any element constant time,
111 * and is acceptable because the order of the channels is
114 channels[i] = channels[--active_channels];
119 }while(i < active_channels);
121 /* Set i = 1, so that we do not place anything over the input_data
122 * element containing our mixed finalizing sounds.
127 /* No finalizing sounds, so no extra inputs. */
131 /* No need to remix if all channels were finalizing. */
132 if(active_channels != 0){
133 register unsigned source_i = 0;
135 input_data[i++] = CIN_MIXER_SOUND_PCM(channels[source_i]) +
136 channels[source_i]->position;
137 channels[source_i]->position += buffer_size;
138 }while(++source_i < active_channels);
140 /* i contains the final element placed into input_data, regardless
141 * of how many were from the input channels.
143 Cin_DSP_Mix(buffer_size, dsp_fmt, i, input_data, buffer);
146 memset(buffer, 0, buffer_size);
149 write(dev, buffer, buffer_size);
151 /* Sleep for 1/10th of a second after the last call to ProcessCommands. */
155 clock_gettime(CLOCK_REALTIME, &now);
156 if(now.tv_sec != tm.tv_sec)
158 ms_diff += (now.tv_nsec - tm.tv_nsec) / 1000000;
162 else if(ms_diff > 99)
167 now.tv_nsec = (100 - ms_diff) * 1000000;
168 nanosleep(&now, NULL);
170 usleep(ms_diff * 1000);