ecere/gui/Window: Prevent uninitialized values if base Window methods not overridden...
[sdk] / audio / alsa.ec
1 #if defined(__linux__)
2 #define uint _uint
3 #include <alsa/asoundlib.h>
4 #undef uint
5
6 import "audio"
7
8 static byte * buffer;
9 static snd_pcm_t *handle;
10 static snd_pcm_sframes_t frames;
11 static AudioSpec audioSpec { };
12 static bool paused;
13 static Semaphore pauseSemaphore { };
14
15 static const char *device = "default";
16
17 static snd_mixer_t *mixer_handle;
18
19 static double volume, balance;
20
21 public void AudioSetBalance(double percent)
22 {
23    balance = percent;
24 }
25
26 public bool AudioSetVolume(VolumeControl type, double percent)
27 {
28    bool result = false;
29    if(type == application)
30    {
31       volume = percent;
32       result = true;
33    }
34    else
35    {
36       snd_mixer_elem_t *elem;
37       snd_mixer_selem_id_t *sid;
38       long pmin, pmax;
39       int set_vol;
40       float f_multi;
41
42         snd_mixer_load(mixer_handle);
43
44       // snd_mixer_selem_id_alloca(&sid);
45       sid = (snd_mixer_selem_id_t *) alloca(snd_mixer_selem_id_sizeof());
46       memset(sid, 0, snd_mixer_selem_id_sizeof());
47
48       snd_mixer_selem_id_set_index(sid, 0);
49       snd_mixer_selem_id_set_name(sid, (type == pcm) ? "PCM" : "Master");
50       elem = snd_mixer_find_selem(mixer_handle, sid);
51       if(elem)
52       {
53          snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
54          f_multi = 1 / (float)(pmax - pmin);
55          set_vol = (int)(percent / f_multi + pmin + 0.5);
56          snd_mixer_selem_set_playback_volume(elem, 0, set_vol);
57          snd_mixer_selem_set_playback_volume(elem, 1, set_vol);
58          result = true;
59       }
60       snd_mixer_free(mixer_handle);
61    }
62    return result;
63 }
64
65 public bool AudioGetVolume(VolumeControl type, double * percent)
66 {
67    bool result = false;
68    if(type == application)
69    {
70       *percent = volume;
71       result = true;
72    }
73    else
74    {
75       snd_mixer_elem_t *elem;
76       snd_mixer_selem_id_t *sid;
77       long pmin, pmax;
78       long set_vol;
79       float f_multi;
80
81         snd_mixer_load(mixer_handle);
82
83       // snd_mixer_selem_id_alloca(&sid);
84       sid = (snd_mixer_selem_id_t *) alloca(snd_mixer_selem_id_sizeof());
85       memset(sid, 0, snd_mixer_selem_id_sizeof());
86
87       snd_mixer_selem_id_set_index(sid, 0);
88       snd_mixer_selem_id_set_name(sid, (type == pcm) ? "PCM" : "Master");
89       elem = snd_mixer_find_selem(mixer_handle, sid);
90
91       if(elem)
92       {
93          snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
94          snd_mixer_selem_get_playback_volume(elem, 0, &set_vol);
95          f_multi = 1 / (float)(pmax - pmin);
96          *percent = f_multi * (set_vol - 0.5 - pmin);
97          result = true;
98       }
99       snd_mixer_free(mixer_handle);
100    }
101    return result;
102 }
103
104 public void OpenMixer()
105 {
106    snd_mixer_open(&mixer_handle, 0);
107    snd_mixer_attach(mixer_handle, "default");
108         snd_mixer_selem_register(mixer_handle, null, null);
109 }
110
111 public void CloseMixer()
112 {
113    snd_mixer_close(mixer_handle);
114 }
115
116 public int OpenAudio(AudioSpec wanted, AudioSpec result)
117 {
118    int err;
119
120    buffer = new byte[wanted.samples * wanted.channels * wanted.bits / 8];
121    memset(buffer, 0, wanted.samples * wanted.channels * wanted.bits / 8);
122
123    if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
124    {
125       printf("Playback open error: %s\n", snd_strerror(err));
126       return 0;
127    }
128    if ((err = snd_pcm_set_params(handle,
129                                  (wanted.bits == 8) ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16,
130                                  SND_PCM_ACCESS_RW_INTERLEAVED,
131                                  wanted.channels,
132                                  wanted.freq,
133                                  1,
134                                  50000)) < 0)
135    {   /* 0.5sec */
136       printf("Playback open error: %s\n", snd_strerror(err));
137       return 0;
138    }
139    soundThread.done = false;
140    result = wanted;
141    audioSpec = wanted;
142    volume = wanted.volume;
143    balance = 0.5;
144    return 1;
145 }
146
147 public void PauseAudio(bool value)
148 {
149    if(value)
150    {
151       soundThread.done = true;
152       soundThread.Wait();
153    }
154    else
155       soundThread.Create();
156 }
157
158 public void CloseAudio()
159 {
160    soundThread.done = true;
161    soundThread.Wait();
162    delete buffer;
163    if(handle) snd_pcm_close(handle);
164 }
165
166 static class SoundThread : Thread
167 {
168    bool done;
169    SoundThread()
170    {
171
172    }
173    ~SoundThread()
174    {
175
176    }
177
178    uint Main()
179    {
180       while(!done)
181       {
182          if(paused)
183          {
184             pauseSemaphore.Wait();
185          }
186          else
187          {
188             int c;
189             short int * samples = (short int *)buffer;
190             double m = volume / (1 + Abs(balance * 2 - 1)) / 100.0;
191             double ll = (2 - (2 * Max(balance, 0.5)))* m;
192             double lr = (-2 * Min(balance, 0.5) + 1) * m;
193             double rl = (2 * Max(balance, 0.5) - 1)  * m;
194             double rr = (2 * Min(balance, 0.5))      * m;
195             uint numSamples = audioSpec.samples/16;
196             // printf("Volume: %f, m : %f, Left: (%f, %f), Right: (%f, %f) \n", volume, m, ll, lr, rl, rr);
197
198             audioSpec.callback(audioSpec.userdata, buffer, numSamples * audioSpec.channels * audioSpec.bits / 8);
199             for(c = 0; c<numSamples; c++)
200             {
201                short sLeft = samples[0], sRight = samples[1];
202                samples[0] = (short)(sLeft * ll + sRight * lr);
203                samples[1] = (short)(sLeft * rl + sRight * rr);
204                samples += 2;
205             }
206
207             frames = snd_pcm_writei(handle, buffer, numSamples);
208             if(frames < 0)
209                frames = snd_pcm_recover(handle, (int)frames, 0);
210             if (frames < 0)
211             {
212                printf("snd_pcm_writei failed: %s\n", snd_strerror((int)frames));
213                break;
214             }
215             if (frames > 0 && frames < numSamples)
216                printf("Short write (expected %ui, wrote %li)\n", numSamples, frames);
217          }
218       }
219       return 0;
220    }
221 }
222
223 static SoundThread soundThread { };
224
225 #endif