~cairo-dock-team/cairo-dock-plug-ins/plug-ins

« back to all changes in this revision

Viewing changes to Impulse/src/Impulse.c

  • Committer: Matthieu Baerts
  • Date: 2014-10-19 00:26:10 UTC
  • Revision ID: matttbe@gmail.com-20141019002610-ulf26s9b4c4rw10r
We just switched from BZR to Git.
Follow us on Github: https://github.com/Cairo-Dock

Note: we will only use Github to manage our source code and all pull requests.
Please continue to report your bugs/ideas/messages on our forum or Launchpad! 

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 *
3
 
 *+  Copyright (c) 2009 Ian Halpern
4
 
 *@  http://impulse.ian-halpern.com
5
 
 *+  Copyright (c) 2011-2012 Matthieu Baerts (Cairo-Dock Project)
6
 
 *@  http://glx-dock.org
7
 
 *
8
 
 *   This file is part of Impulse.
9
 
 *
10
 
 *   Impulse is free software: you can redistribute it and/or modify
11
 
 *   it under the terms of the GNU General Public License as published by
12
 
 *   the Free Software Foundation, either version 3 of the License, or
13
 
 *   (at your option) any later version.
14
 
 *
15
 
 *   Impulse is distributed in the hope that it will be useful,
16
 
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 
 *   GNU General Public License for more details.
19
 
 *
20
 
 *   You should have received a copy of the GNU General Public License
21
 
 *   along with Impulse.  If not, see <http://www.gnu.org/licenses/>.
22
 
 */
23
 
 
24
 
#include <pulse/pulseaudio.h>
25
 
#include <assert.h>
26
 
#include <string.h>
27
 
#include <stdint.h>
28
 
#include <stdio.h>
29
 
#ifdef FFT_IS_AVAILABLE
30
 
#include <fftw3.h>
31
 
#endif
32
 
#include <math.h>
33
 
 
34
 
#include "Impulse.h"
35
 
 
36
 
#define CHUNK 1024
37
 
 
38
 
#ifdef FFT_IS_AVAILABLE
39
 
static const long s_fft_max[] = { 12317168L, 7693595L, 5863615L, 4082974L, 5836037L, 4550263L, 3377914L, 3085778L, 3636534L, 3751823L, 2660548L, 3313252L, 2698853L, 2186441L, 1697466L, 1960070L, 1286950L, 1252382L, 1313726L, 1140443L, 1345589L, 1269153L, 897605L, 900408L, 892528L, 587972L, 662925L, 668177L, 686784L, 656330L, 1580286L, 785491L, 761213L, 730185L, 851753L, 927848L, 891221L, 634291L, 833909L, 646617L, 804409L, 1015627L, 671714L, 813811L, 689614L, 727079L, 853936L, 819333L, 679111L, 730295L, 836287L, 1602396L, 990827L, 773609L, 733606L, 638993L, 604530L, 573002L, 634570L, 1015040L, 679452L, 672091L, 880370L, 1140558L, 1593324L, 686787L, 781368L, 605261L, 1190262L, 525205L, 393080L, 409546L, 436431L, 723744L, 765299L, 393927L, 322105L, 478074L, 458596L, 512763L, 381303L, 671156L, 1177206L, 476813L, 366285L, 436008L, 361763L, 252316L, 204433L, 291331L, 296950L, 329226L, 319209L, 258334L, 388701L, 543025L, 396709L, 296099L, 190213L, 167976L, 138928L, 116720L, 163538L, 331761L, 133932L, 187456L, 530630L, 131474L, 84888L, 82081L, 122379L, 82914L, 75510L, 62669L, 73492L, 68775L, 57121L, 94098L, 68262L, 68307L, 48801L, 46864L, 61480L, 46607L, 45974L, 45819L, 45306L, 45110L, 45175L, 44969L, 44615L, 44440L, 44066L, 43600L, 57117L, 43332L, 59980L, 55319L, 54385L, 81768L, 51165L, 54785L, 73248L, 52494L, 57252L, 61869L, 65900L, 75893L, 65152L, 108009L, 421578L, 152611L, 135307L, 254745L, 132834L, 169101L, 137571L, 141159L, 142151L, 211389L, 267869L, 367730L, 256726L, 185238L, 251197L, 204304L, 284443L, 258223L, 158730L, 228565L, 375950L, 294535L, 288708L, 351054L, 694353L, 477275L, 270576L, 426544L, 362456L, 441219L, 313264L, 300050L, 421051L, 414769L, 244296L, 292822L, 262203L, 418025L, 579471L, 418584L, 419449L, 405345L, 739170L, 488163L, 376361L, 339649L, 313814L, 430849L, 275287L, 382918L, 297214L, 286238L, 367684L, 303578L, 516246L, 654782L, 353370L, 417745L, 392892L, 418934L, 475608L, 284765L, 260639L, 288961L, 301438L, 301305L, 329190L, 252484L, 272364L, 261562L, 208419L, 203045L, 229716L, 191240L, 328251L, 267655L, 322116L, 509542L, 498288L, 341654L, 346341L, 451042L, 452194L, 467716L, 447635L, 644331L, 1231811L, 1181923L, 1043922L, 681166L, 1078456L, 1088757L, 1221378L, 1358397L, 1817252L, 1255182L, 1410357L, 2264454L, 1880361L, 1630934L, 1147988L, 1919954L, 1624734L, 1373554L, 1865118L, 2431931L };
40
 
#else
41
 
#define MAXVALUE 32768 // 2^15
42
 
#endif
43
 
 
44
 
static uint32_t source_index = 0;
45
 
static int16_t buffer[ CHUNK / 2 ], snapshot[ CHUNK / 2 ];
46
 
static size_t buffer_index = 0;
47
 
 
48
 
static pa_context *context = NULL;
49
 
static pa_stream *stream = NULL;
50
 
static pa_threaded_mainloop* mainloop = NULL;
51
 
static pa_io_event* stdio_event = NULL;
52
 
static pa_mainloop_api *mainloop_api = NULL;
53
 
static char *stream_name = NULL, *client_name = NULL, *device = NULL;
54
 
 
55
 
static pa_sample_spec sample_spec = {
56
 
        .format = PA_SAMPLE_S16LE,
57
 
        .rate = 44100,
58
 
        .channels = 2
59
 
};
60
 
 
61
 
static pa_stream_flags_t flags = 0;
62
 
 
63
 
static pa_channel_map channel_map;
64
 
static int channel_map_set = 0;
65
 
 
66
 
/* A shortcut for terminating the application */
67
 
static void quit( int ret ) {
68
 
        assert( mainloop_api );
69
 
        mainloop_api->quit( mainloop_api, ret );
70
 
}
71
 
 
72
 
static void unmute_source_success_cb( pa_context *c, int success, void *userdata ) {
73
 
        // printf("unmute: %d\n", success);
74
 
}
75
 
 
76
 
static void get_source_info_callback( pa_context *c, const pa_source_info *i, int is_last, void *userdata ) {
77
 
 
78
 
        if ( !i )
79
 
                return;
80
 
 
81
 
        // printf("source index: %u\n", i->index );
82
 
 
83
 
        // snprintf(t, sizeof(t), "%u", i->monitor_of_sink);
84
 
 
85
 
//      if ( i->monitor_of_sink != PA_INVALID_INDEX ) {
86
 
                puts( i->name );
87
 
        //      if ( device && strcmp( device, i->name ) == 0 ) return;
88
 
 
89
 
                device = pa_xstrdup( i->name );
90
 
 
91
 
                if ( ( pa_stream_connect_record( stream, device, NULL, flags ) ) < 0 ) {
92
 
                        fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c)));
93
 
                        quit(1);
94
 
                }
95
 
//      }
96
 
}
97
 
 
98
 
/* This is called whenever new data is available */
99
 
static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
100
 
        const void *data;
101
 
        assert(s);
102
 
        assert(length > 0);
103
 
//      printf("stream index: %d\n", pa_stream_get_index( s ) );
104
 
        if (stdio_event)
105
 
                mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
106
 
 
107
 
        if (pa_stream_peek(s, &data, &length) < 0) {
108
 
                fprintf(stderr, "pa_stream_peek() failed: %s\n", pa_strerror(pa_context_errno(context)));
109
 
                quit(1);
110
 
                return;
111
 
        }
112
 
 
113
 
        assert(data);
114
 
        assert(length > 0);
115
 
 
116
 
        int excess = buffer_index * 2 + length - ( CHUNK );
117
 
 
118
 
        if ( excess < 0 ) excess = 0;
119
 
 
120
 
        memcpy( buffer + buffer_index, data, length - excess );
121
 
        buffer_index += ( length - excess ) / 2;
122
 
 
123
 
        if ( excess ) {
124
 
                memcpy( snapshot, buffer, buffer_index * 2 );
125
 
                buffer_index = 0;
126
 
        }
127
 
 
128
 
        pa_stream_drop(s);
129
 
}
130
 
 
131
 
static void stream_state_callback( pa_stream *s, void* userdata );
132
 
 
133
 
static void init_source_stream_for_recording(void) {
134
 
 
135
 
        if (!(stream = pa_stream_new( context, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) {
136
 
                fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(context)));
137
 
                quit(1);
138
 
        }
139
 
 
140
 
        pa_stream_set_read_callback(stream, stream_read_callback, NULL);
141
 
        pa_stream_set_state_callback( stream, stream_state_callback, NULL );
142
 
        pa_operation_unref( pa_context_set_source_mute_by_index( context, source_index, 0, unmute_source_success_cb, NULL ) );
143
 
        pa_operation_unref( pa_context_get_source_info_by_index( context, source_index, get_source_info_callback, NULL ) );
144
 
}
145
 
 
146
 
static void stream_state_callback( pa_stream *s, void* userdata ) {
147
 
        if ( pa_stream_get_state( s ) == PA_STREAM_TERMINATED ) {
148
 
                pa_stream_unref( stream );
149
 
                init_source_stream_for_recording();
150
 
        }
151
 
}
152
 
 
153
 
static void context_state_callback( pa_context *c, void *userdata ) {
154
 
 
155
 
        switch (pa_context_get_state(c)) {
156
 
                case PA_CONTEXT_CONNECTING:
157
 
                case PA_CONTEXT_AUTHORIZING:
158
 
                case PA_CONTEXT_SETTING_NAME:
159
 
                        break;
160
 
                case PA_CONTEXT_READY:
161
 
                        assert(c);
162
 
                        assert(!stream);
163
 
 
164
 
                        /*if (!(stream = pa_stream_new(c, stream_name, &sample_spec, channel_map_set ? &channel_map : NULL))) {
165
 
                                fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
166
 
                                quit(1);
167
 
                        }
168
 
 
169
 
                        pa_stream_set_read_callback(stream, stream_read_callback, NULL);*/
170
 
                        init_source_stream_for_recording();
171
 
 
172
 
                        break;
173
 
                case PA_CONTEXT_TERMINATED:
174
 
                        quit(0);
175
 
                        break;
176
 
 
177
 
                case PA_CONTEXT_FAILED:
178
 
                default:
179
 
                        fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
180
 
                        quit(1);
181
 
        }
182
 
}
183
 
 
184
 
int im_context_state (void)
185
 
{
186
 
        if (context == NULL)
187
 
        return IM_FAILED;
188
 
 
189
 
        switch (pa_context_get_state (context))
190
 
        {
191
 
                case PA_CONTEXT_TERMINATED:
192
 
                case PA_CONTEXT_FAILED:
193
 
                        return IM_FAILED;
194
 
                case PA_CONTEXT_CONNECTING:
195
 
                case PA_CONTEXT_AUTHORIZING:
196
 
                case PA_CONTEXT_SETTING_NAME:
197
 
                default: // default seems to be ok...
198
 
                        return IM_SUCCESS;
199
 
        }
200
 
}
201
 
 
202
 
void im_stop (void) {
203
 
 
204
 
        pa_threaded_mainloop_stop( mainloop );
205
 
 
206
 
        printf( "exit\n" );
207
 
}
208
 
 
209
 
void im_setSourceIndex( uint32_t index ) {
210
 
        source_index = index;
211
 
        if ( !stream ) return;
212
 
 
213
 
        if ( pa_stream_get_state( stream ) != PA_STREAM_UNCONNECTED )
214
 
                pa_stream_disconnect( stream );
215
 
        else
216
 
                init_source_stream_for_recording();
217
 
}
218
 
 
219
 
double *im_getSnapshot (void)
220
 
{
221
 
        static double magnitude [CHUNK / 4];
222
 
 
223
 
#ifdef FFT_IS_AVAILABLE
224
 
        double *in;
225
 
        fftw_complex *out;
226
 
        fftw_plan p;
227
 
 
228
 
        in = (double*) malloc( sizeof( double ) * ( CHUNK / 2 ) );
229
 
        out = (fftw_complex*) fftw_malloc( sizeof( fftw_complex ) * ( CHUNK / 2 ) );
230
 
 
231
 
        if (snapshot != NULL)
232
 
        {
233
 
                int i;
234
 
                for (i = 0; i < CHUNK / 2; i++)
235
 
                {
236
 
                        in[ i ] = (double) snapshot[ i ];
237
 
                }
238
 
        }
239
 
 
240
 
        p = fftw_plan_dft_r2c_1d( CHUNK / 2, in, out, 0 );
241
 
 
242
 
        fftw_execute( p );
243
 
 
244
 
        fftw_destroy_plan( p );
245
 
 
246
 
        if (out != NULL)
247
 
        {
248
 
                int i;
249
 
                for (i = 0; i < CHUNK / 2 / sample_spec.channels; i++)
250
 
                {
251
 
                        magnitude[ i ] = (double) sqrt( pow( out[ i ][ 0 ], 2 ) + pow( out[ i ][ 1 ], 2 ) ) / s_fft_max[ i ];
252
 
                        if (magnitude[ i ] > 1.0 )
253
 
                                magnitude[ i ] = 1.0;
254
 
                }
255
 
        }
256
 
 
257
 
        free( in );
258
 
        fftw_free(out);
259
 
#else
260
 
        int i, iSnapshot, iCurrentMagnitude;
261
 
        for (i = 0; i < CHUNK / 2; i += sample_spec.channels)
262
 
        {
263
 
                iCurrentMagnitude = i / sample_spec.channels; // 1 => 256 (= CHUNK / 2 / channels)
264
 
                magnitude [iCurrentMagnitude] = 0; // init
265
 
                for (int j = 0; j < sample_spec.channels; j++)
266
 
                {
267
 
                        iSnapshot = snapshot [i + j];
268
 
                        if (iSnapshot > 0)
269
 
                                magnitude [iCurrentMagnitude] += (double) iSnapshot / MAXVALUE;
270
 
                }
271
 
                if (magnitude [iCurrentMagnitude] < 10e-5 && iCurrentMagnitude != 0)
272
 
                        magnitude [iCurrentMagnitude] = magnitude [iCurrentMagnitude - 1]; // strange to have 0...
273
 
                magnitude [iCurrentMagnitude] = magnitude [iCurrentMagnitude] / sample_spec.channels / 1.75; // mean value / 1.66 => 
274
 
        }
275
 
#endif
276
 
 
277
 
        return magnitude;
278
 
}
279
 
 
280
 
 
281
 
void im_start ( void ) {
282
 
 
283
 
        // Pulseaudio
284
 
        int r;
285
 
        char *server = NULL;
286
 
 
287
 
        client_name = pa_xstrdup( "impulse" );
288
 
        stream_name = pa_xstrdup( "impulse" );
289
 
 
290
 
        // Set up a new main loop
291
 
 
292
 
        if ( ! ( mainloop = pa_threaded_mainloop_new( ) ) ) {
293
 
                fprintf( stderr, "pa_mainloop_new() failed.\n" );
294
 
                return; // IM_FAILED;
295
 
        }
296
 
 
297
 
        mainloop_api = pa_threaded_mainloop_get_api( mainloop );
298
 
 
299
 
        r = pa_signal_init( mainloop_api );
300
 
        assert( r == 0 );
301
 
 
302
 
        /*if (!(stdio_event = mainloop_api->io_new(mainloop_api,
303
 
                                                                                         STDOUT_FILENO,
304
 
                                                                                         PA_IO_EVENT_OUTPUT,
305
 
                                                                                         stdout_callback, NULL))) {
306
 
                fprintf(stderr, "io_new() failed.\n");
307
 
                goto quit;
308
 
        }*/
309
 
 
310
 
        // create a new connection context
311
 
        if ( ! ( context = pa_context_new( mainloop_api, client_name ) ) ) {
312
 
                fprintf( stderr, "pa_context_new() failed.\n" );
313
 
                return; // IM_FAILED;
314
 
        }
315
 
 
316
 
        pa_context_set_state_callback( context, context_state_callback, NULL );
317
 
 
318
 
        /* Connect the context */
319
 
        pa_context_connect( context, server, 0, NULL );
320
 
 
321
 
        // pulseaudio thread
322
 
        pa_threaded_mainloop_start( mainloop );
323
 
 
324
 
        return; // context_state (context);
325
 
}