OSDN Git Service

Update copyright date
[cinnamon-audio/cinnamon.git] / src / oss / cin_oss.c
1 /* Copyright (c) 2019-2021 AlaskanEmily, 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 #include "cinnamon.h"
9 #include "cin_oss.h"
10 #include "cin_oss_driver.h"
11 #include "cin_mixer_sound.h"
12 #include "cin_dsp.h"
13
14 #define _DEFAULT_SOURCE
15 #define _BSD_SOURCE
16
17 #include <assert.h>
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <time.h>
23
24 /*****************************************************************************/
25
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;
30     struct timespec tm;
31
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;
39     
40     const void *input_data[CIN_OSS_SOUND_CHANNELS+1];
41     
42     void *const buffer = malloc(buffer_size);
43     
44     CIN_DSP_NEW_STACK_FMT_STRUCT(dsp_fmt);
45     
46     CIN_DSP_MIXER_FORMAT_SET(dsp_fmt,
47         format,
48         rate,
49         num_channels);
50     
51 iter:
52     
53     clock_gettime(CLOCK_REALTIME, &tm);
54     
55     if(Cin_OSS_ProcessCommands(drv) == 1){
56         free(buffer);
57         CIN_DSP_FREE_STACK_FMT_STRUCT(dsp_fmt);
58         return NULL;
59     }
60     
61     active_channels = 0;
62     for(i = 0; i < CIN_OSS_SOUND_CHANNELS; i++){
63         struct Cin_MixerSound *const snd = drv_channels[i];
64         if(snd != NULL){
65             channels[active_channels++] = snd;
66             
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;
70             }
71         }
72     }
73     
74     if(active_channels != 0){
75         /* First, mix any finalizing channels (channels without enough data to
76          * fill the buffer)
77          */
78         i = 0;
79         
80         /* Find the first channel which is being finalized, if any. */
81         do{
82             if(channels[i]->position + buffer_size >
83                 channels[i]->byte_len){
84                 break;
85             }
86         }while(++i < active_channels);
87         
88         /* Check this only if we broke early (found a finalizing channel) */
89         if(i != active_channels){
90             input_data[0] = buffer;
91             
92             /* Very fortunately, zero-init is ALSO zero in float and double */
93             memset(buffer, 0, buffer_size);
94             
95             do{
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]) +
101                         snd->position;
102                     
103                     Cin_DSP_Mix(remaining_length,
104                         dsp_fmt,
105                         2,
106                         input_data,
107                         buffer);
108                     
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
112                      * not important.
113                      */
114                     channels[i] = channels[--active_channels];
115                 }
116                 else{
117                     i++;
118                 }
119             }while(i < active_channels);
120             
121             /* Set i = 1, so that we do not place anything over the input_data
122              * element containing our mixed finalizing sounds.
123              */
124             i = 1;
125         }
126         else{
127             /* No finalizing sounds, so no extra inputs. */
128             i = 0;
129         }
130         
131         /* No need to remix if all channels were finalizing. */
132         if(active_channels != 0){
133             register unsigned source_i = 0;
134             do{
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);
139         }
140         /* i contains the final element placed into input_data, regardless
141          * of how many were from the input channels.
142          */
143         Cin_DSP_Mix(buffer_size, dsp_fmt, i, input_data, buffer);
144     }
145     else{
146         memset(buffer, 0, buffer_size);
147     }
148     
149     write(dev, buffer, buffer_size);
150     
151     /* Sleep for 1/10th of a second after the last call to ProcessCommands. */
152     {
153         struct timespec now;
154         long ms_diff = 0;
155         clock_gettime(CLOCK_REALTIME, &now);
156         if(now.tv_sec != tm.tv_sec)
157             ms_diff = 1000;
158         ms_diff += (now.tv_nsec - tm.tv_nsec) / 1000000;
159         
160         if(ms_diff < 0)
161             ms_diff = 0;
162         else if(ms_diff > 99)
163             ms_diff = 99;
164         
165 #ifdef __linux__
166         now.tv_sec = 0;
167         now.tv_nsec = (100 - ms_diff) * 1000000;
168         nanosleep(&now, NULL);
169 #else
170         usleep(ms_diff * 1000);
171 #endif
172     }
173     
174     goto iter;
175 }