1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail : see the 'copyright' file.
6 *
7 * based on 'aplay' from Jaroslav Kysela <perex@perex.cz>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 3
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <stdlib.h>
23 #if !defined(__FreeBSD__) && !defined(__DragonFly__)
24 #include <endian.h>
25 #include <byteswap.h>
26 #else
27 #include <sys/endian.h>
28 #endif
29
30 #include "applet-struct.h"
31 #include "applet-sound.h"
32
33
34 #if __BYTE_ORDER == __LITTLE_ENDIAN
35 #define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
36 #define LE_SHORT(v) (v)
37 #define LE_INT(v) (v)
38 #define BE_SHORT(v) bswap_16(v)
39 #define BE_INT(v) bswap_32(v)
40 #elif __BYTE_ORDER == __BIG_ENDIAN
41 #define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
42 #define LE_SHORT(v) bswap_16(v)
43 #define LE_INT(v) bswap_32(v)
44 #define BE_SHORT(v) (v)
45 #define BE_INT(v) (v)
46 #else
47 #error "Wrong endian"
48 #endif
49
50 #define WAV_RIFF COMPOSE_ID('R','I','F','F')
51 #define WAV_WAVE COMPOSE_ID('W','A','V','E')
52 #define WAV_FMT COMPOSE_ID('f','m','t',' ')
53 #define WAV_DATA COMPOSE_ID('d','a','t','a')
54 #define WAV_PCM_CODE 1
55
56 typedef struct {
57 guint magic; /* 'RIFF' */
58 guint length; /* filelen */
59 guint type; /* 'WAVE' */
60 } WaveHeader;
61
62 typedef struct {
63 gushort format; /* should be 1 for PCM-code */
64 gushort modus; /* 1 Mono, 2 Stereo */
65 guint sample_fq; /* frequence of sample */
66 guint byte_p_sec; // number of bytes/sec (Frequence * BytePerFrame)
67 gushort byte_p_spl; // bytes per frame (NbChannels * BitsPerSample/8)
68 gushort bit_p_spl; // bits per sample (8, 16, 24, 32)
69 } WaveFmtBody;
70
71 typedef struct {
72 guint type; /* 'data' */
73 guint length; /* samplecount */
74 } WaveChunkHeader;
75
76 #define DEFAULT_FORMAT SND_PCM_FORMAT_U8
77 #define DEFAULT_SPEED 8000
78
79
_parse_header(CDSoundFile * pSoundFile)80 static gboolean _parse_header (CDSoundFile *pSoundFile)
81 {
82 gchar *end = pSoundFile->buffer + pSoundFile->length;
83 // check the header
84 if (pSoundFile->length < sizeof(WaveHeader) + sizeof (WaveChunkHeader) + sizeof (WaveFmtBody)) // minimum size.
85 return FALSE;
86 gchar *ptr = pSoundFile->buffer;
87 WaveHeader *h = (WaveHeader *)ptr;
88 if (h->magic != WAV_RIFF || h->type != WAV_WAVE)
89 return FALSE;
90 ptr += sizeof(WaveHeader);
91
92 // check the audio format
93 guint type, len;
94 WaveChunkHeader *c;
95 do
96 {
97 c = (WaveChunkHeader*) ptr;
98 type = c->type;
99 len = LE_INT(c->length);
100 len += len % 2;
101 if (type == WAV_FMT)
102 break;
103 ptr += sizeof (WaveChunkHeader) + len;
104 } while (ptr < end);
105 g_return_val_if_fail (ptr < end, FALSE);
106
107 ptr += sizeof (WaveChunkHeader);
108 WaveFmtBody *f = (WaveFmtBody*) ptr;
109
110 if (len < sizeof(WaveFmtBody))
111 {
112 cd_warning ("unknown length of 'fmt ' chunk (read %u, should be %u at least)",
113 len, (guint)sizeof(WaveFmtBody));
114 return FALSE;
115 }
116
117 gint format = LE_SHORT(f->format);
118 gint channels = LE_SHORT(f->modus);
119 gint rate = LE_INT(f->sample_fq); // frames/s
120 gint bytePerSec = LE_INT(f->byte_p_sec);
121 gint bytePerBloc = LE_SHORT(f->byte_p_spl);
122 gint bitsPerSample = LE_SHORT(f->bit_p_spl);
123
124 if (format != WAV_PCM_CODE)
125 {
126 cd_warning ("can't play not PCM-coded WAVE-files");
127 return FALSE;
128 }
129 if (LE_SHORT(f->modus) < 1)
130 {
131 cd_warning ("can't play WAVE-files with %d tracks", LE_SHORT(f->modus));
132 return FALSE;
133 }
134 pSoundFile->channels = LE_SHORT(f->modus);
135
136 switch (LE_SHORT(f->bit_p_spl)) {
137 case 8:
138 if (pSoundFile->format != DEFAULT_FORMAT &&
139 pSoundFile->format != SND_PCM_FORMAT_U8)
140 cd_warning ("Warning: format is changed to U8\n");
141 pSoundFile->format = SND_PCM_FORMAT_U8;
142 break;
143 case 16:
144 if (pSoundFile->format != DEFAULT_FORMAT &&
145 pSoundFile->format != SND_PCM_FORMAT_S16_LE)
146 cd_warning ("Warning: format is changed to S16_LE\n");
147 pSoundFile->format = SND_PCM_FORMAT_S16_LE;
148 break;
149 case 24:
150 switch (LE_SHORT(f->byte_p_spl) / pSoundFile->channels) {
151 case 3:
152 if (pSoundFile->format != DEFAULT_FORMAT &&
153 pSoundFile->format != SND_PCM_FORMAT_S24_3LE)
154 cd_warning ("Warning: format is changed to S24_3LE\n");
155 pSoundFile->format = SND_PCM_FORMAT_S24_3LE;
156 break;
157 case 4:
158 if (pSoundFile->format != DEFAULT_FORMAT &&
159 pSoundFile->format != SND_PCM_FORMAT_S24_LE)
160 cd_warning ("Warning: format is changed to S24_LE\n");
161 pSoundFile->format = SND_PCM_FORMAT_S24_LE;
162 break;
163 default:
164 cd_warning (" can't play WAVE-files with sample %d bits in %d bytes wide (%d channels)",
165 LE_SHORT(f->bit_p_spl), LE_SHORT(f->byte_p_spl), pSoundFile->channels);
166 return FALSE;
167 }
168 break;
169 case 32:
170 pSoundFile->format = SND_PCM_FORMAT_S32_LE;
171 break;
172 default:
173 cd_warning (" can't play WAVE-files with sample %d bits wide",
174 LE_SHORT(f->bit_p_spl));
175 return FALSE;
176 }
177
178 cd_debug ("rate: %d; channels: %d; BytePerSec: %d; BytePerBloc: %d; BitsPerSample: %d", rate, channels, bytePerSec, bytePerBloc, bitsPerSample);
179 pSoundFile->rate = LE_INT(rate);
180
181 ptr += len;
182 // check the audio data
183 while (ptr < end)
184 {
185 c = (WaveChunkHeader*) ptr;
186 type = c->type;
187 len = LE_INT(c->length);
188 len += len % 2;
189 if (type == WAV_DATA)
190 break;
191 ptr += sizeof (WaveChunkHeader) + len;
192 };
193 g_return_val_if_fail (ptr < end, FALSE);
194
195 cd_debug ("len = %d; file size = %d", len, pSoundFile->length);
196 pSoundFile->size = len;
197 pSoundFile->iNbFrames = pSoundFile->size / bytePerBloc;
198 pSoundFile->iBitsPerSample = bitsPerSample;
199 pSoundFile->fTimelength = (double)pSoundFile->size / bytePerSec;
200 ptr += sizeof (WaveChunkHeader);
201 pSoundFile->data = ptr;
202
203 return TRUE;
204 }
205
206 typedef struct {
207 char a;
208 char b;
209 char c;
210 } int24;
211
cd_sound_load_sound_file(const gchar * cFilePath)212 CDSoundFile *cd_sound_load_sound_file (const gchar *cFilePath)
213 {
214 // read the file at once
215 gchar *buffer = NULL;
216 gsize length = 0;
217 g_file_get_contents (cFilePath,
218 &buffer,
219 &length,
220 NULL);
221 g_return_val_if_fail (buffer != NULL, NULL);
222
223 CDSoundFile *pSoundFile = g_new0 (CDSoundFile, 1);
224 pSoundFile->buffer = buffer;
225 pSoundFile->length = length;
226 pSoundFile->format = DEFAULT_FORMAT;
227 pSoundFile->rate = DEFAULT_SPEED;
228 pSoundFile->channels = 1;
229
230 // parse the header to get the info
231 _parse_header (pSoundFile);
232
233 // apply volume
234 if (myConfig.fVolume < .99)
235 {
236 int iNbSamples = pSoundFile->size * 8 / pSoundFile->iBitsPerSample;
237 int i;
238 /**gchar *data = pSoundFile->data;
239 cd_debug ("%d samples", iNbSamples);
240 gint32 sample;
241 gint spl;
242 gint32 *psample;
243 for (i = 0; i < iNbSamples-3; i++)
244 {
245 psample = (gint32 *)&data[i*pSoundFile->iBitsPerSample/8];
246 sample = *psample;
247 spl = sample & (0xFFFFFFFF >> (32 - pSoundFile->iBitsPerSample));
248 spl *= myConfig.fVolume;
249 *psample = (sample & (0xFFFFFFFF << pSoundFile->iBitsPerSample)) + spl;
250 }*/
251
252 // .1 -> /2
253 // (1 - vol) * 10 + 1
254 double v = (1 - myConfig.fVolume) * 10 + 1;
255 switch (pSoundFile->iBitsPerSample)
256 {
257 case 8:
258 {
259 char *data = (char*)pSoundFile->data;
260 char *psample;
261 for (i = 0; i < iNbSamples; i++)
262 {
263 psample = &data[i];
264 *psample /= v;
265 }
266 }
267 break;
268 case 16:
269 {
270 gint16 *data = (gint16*)pSoundFile->data;
271 gint16 *psample;
272 for (i = 0; i < iNbSamples; i++)
273 {
274 psample = &data[i];
275 *psample /= v;
276 }
277 }
278 break;
279 case 24:
280 {
281 int24 *data = (int24*)pSoundFile->data;
282 int24 sample24, *psample;
283 gint32 sample;
284 for (i = 0; i < iNbSamples; i++)
285 {
286 psample = &data[i];
287 sample24 = *psample;
288 sample = (sample24.a<<16) + (sample24.b<<8) + sample24.c;
289 sample /= v;
290 sample24.a = (sample & 0x00FF0000) >> 16;
291 sample24.b = (sample & 0x0000FF00) >> 8;
292 sample24.c = sample & 0x000000FF;
293 *psample = sample24;
294 }
295 }
296 break;
297 case 32:
298 {
299 gint32 *data = (gint32*)pSoundFile->data;
300 gint32 *psample;
301 for (i = 0; i < iNbSamples; i++)
302 {
303 psample = &data[i];
304 *psample /= v;
305 }
306 }
307 break;
308 default:
309 break;
310 }
311 }
312
313 return pSoundFile;
314 }
315
316
317
set_params(snd_pcm_t * handle,CDSoundFile * pSoundFile)318 static gboolean set_params (snd_pcm_t *handle, CDSoundFile *pSoundFile)
319 {
320 snd_pcm_sw_params_t *swparams;
321 snd_pcm_uframes_t buffer_size;
322 int err;
323 size_t n;
324 unsigned int rate;
325 snd_pcm_uframes_t start_threshold, stop_threshold;
326 static unsigned period_time = 0;
327 static unsigned buffer_time = 0;
328 static snd_pcm_uframes_t chunk_size = 0;
329 static size_t bits_per_sample, bits_per_frame;
330
331 // initiate hardware params
332 snd_pcm_hw_params_t *params;
333 snd_pcm_hw_params_alloca (¶ms);
334 err = snd_pcm_hw_params_any(handle, params);
335 if (err < 0)
336 {
337 cd_warning ("Broken configuration for this PCM: no configurations available");
338 return FALSE;
339 }
340 err = snd_pcm_hw_params_set_access (handle, params,
341 SND_PCM_ACCESS_RW_INTERLEAVED);
342 if (err < 0)
343 {
344 cd_warning ("Access type not available");
345 return FALSE;
346 }
347
348 // set format
349 err = snd_pcm_hw_params_set_format(handle, params, pSoundFile->format);
350 if (err < 0)
351 {
352 cd_warning ("Sample format non available");
353 return FALSE;
354 }
355 // set channels number
356 err = snd_pcm_hw_params_set_channels(handle, params, pSoundFile->channels);
357 if (err < 0)
358 {
359 cd_warning ("Channels count non available");
360 return FALSE;
361 }
362
363 // set rate
364 rate = pSoundFile->rate;
365 err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0);
366 assert(err >= 0);
367 if ((float)rate * 1.05 < pSoundFile->rate || (float)rate * 0.95 > pSoundFile->rate)
368 {
369 cd_warning ("rate is not accurate (requested = %iHz, got = %iHz)\n", pSoundFile->rate, rate);
370 }
371 pSoundFile->rate = rate;
372
373 // set buffer and period time
374 err = snd_pcm_hw_params_get_buffer_time_max(params,
375 &buffer_time, 0);
376 assert(err >= 0);
377 if (buffer_time > 500000)
378 buffer_time = 500000;
379
380 period_time = buffer_time / 4;
381
382 err = snd_pcm_hw_params_set_period_time_near(handle, params,
383 &period_time, 0);
384 assert(err >= 0);
385 err = snd_pcm_hw_params_set_buffer_time_near(handle, params,
386 &buffer_time, 0);
387 assert(err >= 0);
388
389 // now set paramas to the handle
390 err = snd_pcm_hw_params(handle, params);
391 if (err < 0)
392 {
393 cd_warning ("Unable to install hw params:");
394 return FALSE;
395 }
396
397 snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
398 snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
399 if (chunk_size == buffer_size) {
400 cd_warning ("Can't use period equal to buffer size (%lu == %lu)",
401 chunk_size, buffer_size);
402 return FALSE;
403 }
404
405 // software params
406 snd_pcm_sw_params_alloca(&swparams);
407 snd_pcm_sw_params_current(handle, swparams);
408 n = chunk_size;
409 err = snd_pcm_sw_params_set_avail_min(handle, swparams, n);
410
411 /* round up to closest transfer boundary */
412 n = buffer_size;
413 start_threshold = n;
414 err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
415 assert(err >= 0);
416 stop_threshold = buffer_size;
417 err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
418 assert(err >= 0);
419
420 if (snd_pcm_sw_params(handle, swparams) < 0)
421 {
422 cd_warning ("unable to install sw params:");
423 return FALSE;
424 }
425
426 bits_per_sample = snd_pcm_format_physical_width(pSoundFile->format);
427 bits_per_frame = bits_per_sample * pSoundFile->channels;
428
429 cd_debug ("bits_per_frame: %d; rate: %d", bits_per_frame, pSoundFile->rate);
430 cd_debug ("chunk_size: %d; buffer_size: %d", chunk_size, buffer_size);
431 pSoundFile->iNbFrames = (pSoundFile->size * 8) / bits_per_frame;
432 return TRUE;
433 }
434
435
_free_shared_memory(CDSharedMemory * pSharedMemory)436 static void _free_shared_memory (CDSharedMemory *pSharedMemory)
437 {
438 // close the handle
439 if (pSharedMemory->handle)
440 snd_pcm_close (pSharedMemory->handle);
441
442 // remove the task from the list of tasks
443 myData.pTasks = g_list_remove (myData.pTasks, pSharedMemory->pTask);
444 g_free (pSharedMemory);
445 }
446
_finish_play_sound(CDSharedMemory * pSharedMemory)447 static gboolean _finish_play_sound (CDSharedMemory *pSharedMemory)
448 {
449 gldi_task_discard (pSharedMemory->pTask);
450 return FALSE;
451 }
452
_play_sound_async(CDSharedMemory * pSharedMemory)453 static void _play_sound_async (CDSharedMemory *pSharedMemory)
454 {
455 CDSoundFile *pSoundFile = pSharedMemory->pSoundFile;
456 g_return_if_fail (pSoundFile != NULL);
457 // open the device
458 snd_pcm_t *handle = NULL;
459 int err = snd_pcm_open (&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); // open-mode = 0
460 if (err < 0)
461 {
462 cd_warning ("audio open error: %s", snd_strerror(err));
463 return;
464 }
465
466 // set params
467 if ( !set_params (handle, pSoundFile))
468 return;
469
470 // play data
471 ssize_t r;
472 int n = pSoundFile->iNbFrames;
473 gchar *buffer = pSoundFile->data;
474 while (n > 0)
475 {
476 r = snd_pcm_writei (handle, buffer, n);
477 if (r == -EAGAIN || (r >= 0 && r < n))
478 {
479 snd_pcm_wait (handle, 100);
480 }
481 else if (r == -EPIPE) // an underrun occurred
482 {
483 cd_debug ("underrun");
484 snd_pcm_status_t *status;
485 int res;
486 snd_pcm_status_alloca(&status);
487 if ((res = snd_pcm_status(handle, status))<0)
488 {
489 cd_warning ("status error: %s", snd_strerror(res));
490 return;
491 }
492 snd_pcm_state_t state = snd_pcm_status_get_state(status);
493
494 if (state == SND_PCM_STATE_XRUN)
495 {
496 if ((res = snd_pcm_prepare (handle)) < 0)
497 {
498 cd_warning ("prepare error: %s", snd_strerror(res));
499 return;
500 }
501 continue; // ok, data should be accepted again
502 }
503 else if (state != SND_PCM_STATE_DRAINING)
504 {
505 cd_warning ("read/write error, state = %s", snd_pcm_state_name(state));
506 return;
507 }
508 }
509 else if (r == -ESTRPIPE) // a suspend event occurred (stream is suspended and waiting for an application recovery)
510 {
511 cd_debug ("suspend");
512 int res;
513 while ((res = snd_pcm_resume (handle)) == -EAGAIN)
514 sleep(1); // wait until suspend flag is released
515 if (res < 0) // failed, restart stream
516 {
517 if ((res = snd_pcm_prepare(handle)) < 0)
518 {
519 cd_warning ("suspend: prepare error: %s", snd_strerror(res));
520 return ;
521 }
522 }
523 }
524 else if (r < 0)
525 {
526 cd_warning ("write error: %s", snd_strerror(r));
527 return ;
528 }
529 if (r > 0)
530 {
531 n -= r;
532 buffer += r;
533 }
534 }
535
536 pSharedMemory->handle = handle;
537 snd_pcm_drain (handle); // flush data
538 }
539
cd_sound_play_sound(CDSoundFile * pSoundFile)540 void cd_sound_play_sound (CDSoundFile *pSoundFile)
541 {
542 CDSharedMemory *pSharedMemory = g_new0 (CDSharedMemory, 1);
543 pSharedMemory->pSoundFile = pSoundFile; // the sound file is loaded before, and will stay alive until the task is stopped.
544 GldiTask *pTask = gldi_task_new_full (0, // 1 shot task.
545 (GldiGetDataAsyncFunc) _play_sound_async,
546 (GldiUpdateSyncFunc) _finish_play_sound,
547 (GFreeFunc) _free_shared_memory,
548 pSharedMemory);
549 pSharedMemory->pTask = pTask;
550 myData.pTasks = g_list_prepend (myData.pTasks, pTask);
551
552 gldi_task_launch (pTask);
553 }
554
555
cd_sound_free_sound_file(CDSoundFile * pSoundFile)556 void cd_sound_free_sound_file (CDSoundFile *pSoundFile)
557 {
558 if (!pSoundFile)
559 return;
560 g_free (pSoundFile->buffer); // 'data' points inside 'buffer'.
561 g_free (pSoundFile);
562
563 }
564
cd_sound_free_current_tasks(void)565 void cd_sound_free_current_tasks (void)
566 {
567 GldiTask *pTask;
568 GList *t = myData.pTasks, *next_t;
569 while (t != NULL)
570 {
571 next_t = t->next;
572 pTask = t->data;
573 gldi_task_free (pTask); // will remove it from the list.
574 t = next_t;
575 } // at this point the list is empty and myData.pTasks is NULL.
576 }
577