3
*+ Copyright (c) 2009 Ian Halpern
4
*@ http://impulse.ian-halpern.com
5
*+ Copyright (c) 2011-2012 Matthieu Baerts (Cairo-Dock Project)
8
* This file is part of Impulse.
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.
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.
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/>.
24
#include <pulse/pulseaudio.h>
29
#ifdef FFT_IS_AVAILABLE
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 };
41
#define MAXVALUE 32768 // 2^15
44
static uint32_t source_index = 0;
45
static int16_t buffer[ CHUNK / 2 ], snapshot[ CHUNK / 2 ];
46
static size_t buffer_index = 0;
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;
55
static pa_sample_spec sample_spec = {
56
.format = PA_SAMPLE_S16LE,
61
static pa_stream_flags_t flags = 0;
63
static pa_channel_map channel_map;
64
static int channel_map_set = 0;
66
/* A shortcut for terminating the application */
67
static void quit( int ret ) {
68
assert( mainloop_api );
69
mainloop_api->quit( mainloop_api, ret );
72
static void unmute_source_success_cb( pa_context *c, int success, void *userdata ) {
73
// printf("unmute: %d\n", success);
76
static void get_source_info_callback( pa_context *c, const pa_source_info *i, int is_last, void *userdata ) {
81
// printf("source index: %u\n", i->index );
83
// snprintf(t, sizeof(t), "%u", i->monitor_of_sink);
85
// if ( i->monitor_of_sink != PA_INVALID_INDEX ) {
87
// if ( device && strcmp( device, i->name ) == 0 ) return;
89
device = pa_xstrdup( i->name );
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)));
98
/* This is called whenever new data is available */
99
static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
103
// printf("stream index: %d\n", pa_stream_get_index( s ) );
105
mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
107
if (pa_stream_peek(s, &data, &length) < 0) {
108
fprintf(stderr, "pa_stream_peek() failed: %s\n", pa_strerror(pa_context_errno(context)));
116
int excess = buffer_index * 2 + length - ( CHUNK );
118
if ( excess < 0 ) excess = 0;
120
memcpy( buffer + buffer_index, data, length - excess );
121
buffer_index += ( length - excess ) / 2;
124
memcpy( snapshot, buffer, buffer_index * 2 );
131
static void stream_state_callback( pa_stream *s, void* userdata );
133
static void init_source_stream_for_recording(void) {
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)));
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 ) );
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();
153
static void context_state_callback( pa_context *c, void *userdata ) {
155
switch (pa_context_get_state(c)) {
156
case PA_CONTEXT_CONNECTING:
157
case PA_CONTEXT_AUTHORIZING:
158
case PA_CONTEXT_SETTING_NAME:
160
case PA_CONTEXT_READY:
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)));
169
pa_stream_set_read_callback(stream, stream_read_callback, NULL);*/
170
init_source_stream_for_recording();
173
case PA_CONTEXT_TERMINATED:
177
case PA_CONTEXT_FAILED:
179
fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
184
int im_context_state (void)
189
switch (pa_context_get_state (context))
191
case PA_CONTEXT_TERMINATED:
192
case PA_CONTEXT_FAILED:
194
case PA_CONTEXT_CONNECTING:
195
case PA_CONTEXT_AUTHORIZING:
196
case PA_CONTEXT_SETTING_NAME:
197
default: // default seems to be ok...
202
void im_stop (void) {
204
pa_threaded_mainloop_stop( mainloop );
209
void im_setSourceIndex( uint32_t index ) {
210
source_index = index;
211
if ( !stream ) return;
213
if ( pa_stream_get_state( stream ) != PA_STREAM_UNCONNECTED )
214
pa_stream_disconnect( stream );
216
init_source_stream_for_recording();
219
double *im_getSnapshot (void)
221
static double magnitude [CHUNK / 4];
223
#ifdef FFT_IS_AVAILABLE
228
in = (double*) malloc( sizeof( double ) * ( CHUNK / 2 ) );
229
out = (fftw_complex*) fftw_malloc( sizeof( fftw_complex ) * ( CHUNK / 2 ) );
231
if (snapshot != NULL)
234
for (i = 0; i < CHUNK / 2; i++)
236
in[ i ] = (double) snapshot[ i ];
240
p = fftw_plan_dft_r2c_1d( CHUNK / 2, in, out, 0 );
244
fftw_destroy_plan( p );
249
for (i = 0; i < CHUNK / 2 / sample_spec.channels; i++)
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;
260
int i, iSnapshot, iCurrentMagnitude;
261
for (i = 0; i < CHUNK / 2; i += sample_spec.channels)
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++)
267
iSnapshot = snapshot [i + j];
269
magnitude [iCurrentMagnitude] += (double) iSnapshot / MAXVALUE;
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 =>
281
void im_start ( void ) {
287
client_name = pa_xstrdup( "impulse" );
288
stream_name = pa_xstrdup( "impulse" );
290
// Set up a new main loop
292
if ( ! ( mainloop = pa_threaded_mainloop_new( ) ) ) {
293
fprintf( stderr, "pa_mainloop_new() failed.\n" );
294
return; // IM_FAILED;
297
mainloop_api = pa_threaded_mainloop_get_api( mainloop );
299
r = pa_signal_init( mainloop_api );
302
/*if (!(stdio_event = mainloop_api->io_new(mainloop_api,
305
stdout_callback, NULL))) {
306
fprintf(stderr, "io_new() failed.\n");
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;
316
pa_context_set_state_callback( context, context_state_callback, NULL );
318
/* Connect the context */
319
pa_context_connect( context, server, 0, NULL );
322
pa_threaded_mainloop_start( mainloop );
324
return; // context_state (context);