diff --git a/sflphone-common/src/audio/pulseaudio/audiostream.cpp b/sflphone-common/src/audio/pulseaudio/audiostream.cpp index ac51fa0d54f370966051b6f29fd76e1ead3fd01f..e743c85286d9e3e16292f6d4f4de478f9d714143 100644 --- a/sflphone-common/src/audio/pulseaudio/audiostream.cpp +++ b/sflphone-common/src/audio/pulseaudio/audiostream.cpp @@ -72,7 +72,7 @@ bool AudioStream::drainStream (void) { if (_audiostream) { - _debug ("Draining stream"); + _info("Audio: Draining stream"); pa_operation * operation; pa_threaded_mainloop_lock (_mainloop); @@ -80,7 +80,7 @@ AudioStream::drainStream (void) if ( (operation = pa_stream_drain (_audiostream, success_cb, _mainloop))) { while (pa_operation_get_state (operation) != PA_OPERATION_DONE) { if (!_context || pa_context_get_state (_context) != PA_CONTEXT_READY || !_audiostream || pa_stream_get_state (_audiostream) != PA_STREAM_READY) { - _debug ("Connection died: %s", _context ? pa_strerror (pa_context_errno (_context)) : "NULL"); + _warn("Audio: Connection died: %s", _context ? pa_strerror (pa_context_errno (_context)) : "NULL"); pa_operation_unref (operation); break; } else { @@ -98,7 +98,7 @@ AudioStream::drainStream (void) bool AudioStream::disconnectStream (void) { - _debug ("Destroy audio streams"); + _info("Audio: Destroy audio streams"); pa_threaded_mainloop_lock (_mainloop); @@ -127,7 +127,7 @@ AudioStream::stream_state_callback (pa_stream* s, void* user_data) { pa_threaded_mainloop *m; - _debug ("AudioStream::stream_state_callback :: The state of the stream changed"); + _info("Audio: The state of the stream changed"); assert (s); m = (pa_threaded_mainloop*) user_data; @@ -136,26 +136,25 @@ AudioStream::stream_state_callback (pa_stream* s, void* user_data) switch (pa_stream_get_state (s)) { case PA_STREAM_CREATING: - _debug ("Stream is creating..."); + _info("Audio: Stream is creating..."); break; case PA_STREAM_TERMINATED: - _debug ("Stream is terminating..."); + _info ("Audio: Stream is terminating..."); break; case PA_STREAM_READY: - _debug ("Stream successfully created, connected to %s", pa_stream_get_device_name (s)); - // pa_stream_cork( s, 0, NULL, NULL); + _info ("Audio: Stream successfully created, connected to %s", pa_stream_get_device_name (s)); break; case PA_STREAM_UNCONNECTED: - _debug ("Stream unconnected"); + _info ("Audio: Stream unconnected"); break; case PA_STREAM_FAILED: default: - _debug ("Stream error - Sink/Source doesn't exists: %s" , pa_strerror (pa_context_errno (pa_stream_get_context (s)))); + _warn("Audio: Error - Sink/Source doesn't exists: %s" , pa_strerror (pa_context_errno (pa_stream_get_context (s)))); exit (0); break; } @@ -181,45 +180,45 @@ AudioStream::createStream (pa_context* c) assert (pa_sample_spec_valid (&_sample_spec)); assert (pa_channel_map_valid (&channel_map)); + _info("Audio: Create pulseaudio stream"); + pa_buffer_attr* attributes = (pa_buffer_attr*) malloc (sizeof (pa_buffer_attr)); + if (! (s = pa_stream_new (c, _streamDescription.c_str() , &_sample_spec, &channel_map))) - _debug ("%s: pa_stream_new() failed : %s" , _streamDescription.c_str(), pa_strerror (pa_context_errno (c))); + _warn ("Audio: Error: %s: pa_stream_new() failed : %s" , _streamDescription.c_str(), pa_strerror (pa_context_errno (c))); assert (s); - // parameters are defined as number of bytes - // 2048 bytes (1024 int16) is 20 ms at 44100 Hz if (_streamType == PLAYBACK_STREAM) { - // 20 ms framesize TODO: take framesize value from config attributes->maxlength = (uint32_t) -1; attributes->tlength = pa_usec_to_bytes (100 * PA_USEC_PER_MSEC, &_sample_spec); attributes->prebuf = 0; attributes->minreq = (uint32_t) -1; - attributes->fragsize = (uint32_t) -1; + pa_threaded_mainloop_lock(_mainloop); pa_stream_connect_playback (s , NULL , attributes, (pa_stream_flags_t)(PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE), &_volume, NULL); + pa_threaded_mainloop_unlock(_mainloop); + } else if (_streamType == CAPTURE_STREAM) { - // 20 ms framesize TODO: take framesize value from config attributes->maxlength = (uint32_t) -1; - attributes->tlength = (uint32_t) -1; - attributes->prebuf = (uint32_t) -1; - attributes->minreq = (uint32_t) -1; attributes->fragsize = pa_usec_to_bytes (50 * PA_USEC_PER_MSEC, &_sample_spec); - - - pa_stream_connect_record (s, NULL, attributes, (pa_stream_flags_t) (PA_STREAM_PEAK_DETECT|PA_STREAM_ADJUST_LATENCY)); + pa_threaded_mainloop_lock(_mainloop); + pa_stream_connect_record (s, NULL, attributes, (pa_stream_flags_t) (PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE)); + pa_threaded_mainloop_unlock(_mainloop); + } else if (_streamType == UPLOAD_STREAM) { pa_stream_connect_upload (s , 1024); } else { - _debug ("Stream type unknown "); + _warn ("Audio: Error: Stream type unknown "); } pa_stream_set_state_callback (s , stream_state_callback, _mainloop); + free (attributes); return s; diff --git a/sflphone-common/src/audio/pulseaudio/pulselayer.cpp b/sflphone-common/src/audio/pulseaudio/pulselayer.cpp index a9c03e2ed2ac90332252b4e8cb9229be95d7bd21..e0aca6a3fb0133a1be689061e4801d79bbb3fd0a 100644 --- a/sflphone-common/src/audio/pulseaudio/pulselayer.cpp +++ b/sflphone-common/src/audio/pulseaudio/pulselayer.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Savoir-Faire Linux inc. - * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,62 +25,47 @@ int framesPerBuffer = 2048; static void playback_callback (pa_stream* s, size_t bytes, void* userdata) { - // _debug("playback_callback"); assert (s && bytes); assert (bytes > 0); static_cast<PulseLayer*> (userdata)->processPlaybackData(); - // static_cast<PulseLayer*> (userdata)->processData(); + } static void capture_callback (pa_stream* s, size_t bytes, void* userdata) { - // _debug("capture_callback"); assert (s && bytes); assert (bytes > 0); static_cast<PulseLayer*> (userdata)->processCaptureData(); - // static_cast<PulseLayer*> (userdata)->processData(); + } /* static void stream_suspended_callback (pa_stream *s UNUSED, void *userdata UNUSED) { - _debug("PulseLayer::Stream Suspended"); + _debug("Audio: Stream Suspended"); } */ /* static void stream_moved_callback(pa_stream *s UNUSED, void *userdata UNUSED) { - _debug("PulseLayer::Stream Moved"); + _debug("Audio: Stream Moved"); } */ static void playback_underflow_callback (pa_stream* s, void* userdata UNUSED) { - _debug ("PulseLayer::Buffer Underflow"); - // const pa_timing_info* info = pa_stream_get_timing_info(s); - // _debug(" pa write_index: %l", (long)(info->write_index)); - // _debug(" pa write_index_corupt (if not 0): %i", info->write_index_corrupt); - // _debug(" pa read_index: %l", (long)(info->read_index)); - // _debug(" pa read_index_corrupt (if not 0): %i", info->read_index_corrupt); - - - // fill in audio buffer twice the prebuffering value to restart playback - pa_stream_writable_size (s); - pa_stream_trigger (s, NULL, NULL); - + _debug ("Audio: Buffer Underflow"); } static void playback_overflow_callback (pa_stream* s UNUSED, void* userdata UNUSED) { - _debug ("PulseLayer::Buffer OverFlow"); - //PulseLayer* pulse = (PulseLayer*) userdata; - // pa_stream_drop (s); - // pa_stream_trigger (s, NULL, NULL); + _debug ("Audio: Buffer OverFlow"); + } @@ -91,11 +76,11 @@ PulseLayer::PulseLayer (ManagerImpl* manager) , playback() , record() { - _debug ("PulseLayer::Pulse audio constructor: Create context"); - _urgentRingBuffer.createReadPointer(); dcblocker = new DcBlocker(); is_started = false; + + openLayer(); } // Destructor @@ -113,28 +98,59 @@ PulseLayer::~PulseLayer (void) dcblocker = NULL; } +void +PulseLayer::openLayer (void) +{ + if (!is_started) { + + _info("Audio: Open layer"); + + if (!m) { + + _info("Audio: Creating PulseAudio mainloop"); + if (!(m = pa_threaded_mainloop_new())) + _warn ("Audio: Error: while creating pulseaudio mainloop"); + + if (pa_threaded_mainloop_start (m) < 0) { + _warn("Audio: Error: Failed to start pulseaudio mainloop"); + } + + assert(m); + } + + if (!context) { + + _info("Audio: Creating new PulseAudio Context"); + pa_threaded_mainloop_lock (m); + + // Instanciate a context + if (! (context = pa_context_new (pa_threaded_mainloop_get_api (m) , "SFLphone"))) + _warn ("Audio: Error: while creating pulseaudio context"); + + pa_threaded_mainloop_unlock (m); + + assert (context); + } + + // Create Streams + connectPulseAudioServer(); + + is_started = true; + } + +} + bool PulseLayer::closeLayer (void) { - _debug ("PulseLayer::closeLayer :: Destroy pulselayer"); - - // Commenting the line below will make the - // PulseLayer to close immediately, not - // waiting for the playback buffer to be - // emptied. It should not hurt. - //playback->drainStream(); + _info("Audio: Destroy pulselayer"); + disconnectAudioStream(); + if (m) { pa_threaded_mainloop_stop (m); } - // playback->disconnectStream(); - // closePlaybackStream(); - - // record->disconnectStream(); - // closeCaptureStream(); - - // disconnectAudioStream(); if (context) { pa_context_disconnect (context); @@ -153,7 +169,7 @@ PulseLayer::closeLayer (void) void PulseLayer::connectPulseAudioServer (void) { - _debug ("PulseLayer::connectPulseAudioServer "); + _info("Audio: connect to pulseaudio server"); setenv ("PULSE_PROP_media.role", "phone", 1); @@ -161,30 +177,27 @@ PulseLayer::connectPulseAudioServer (void) pa_threaded_mainloop_lock (m); - _debug ("Connect the context to the server"); + _info("Audio: Connect the context to the server"); pa_context_connect (context, NULL , flag , NULL); pa_context_set_state_callback (context, context_state_callback, this); - pa_threaded_mainloop_wait (m); - + pa_threaded_mainloop_wait(m); + + // Run the main loop - if (pa_context_get_state (context) != PA_CONTEXT_READY) { - _debug ("Error connecting to pulse audio server"); - // pa_threaded_mainloop_unlock (m); + _warn("Audio: Error: connecting to pulse audio server"); } pa_threaded_mainloop_unlock (m); - //serverinfo(); - //muteAudioApps(99); - _debug ("Context creation done"); + _info("Audio: Context creation done"); } void PulseLayer::context_state_callback (pa_context* c, void* user_data) { - _debug ("PulseLayer::context_state_callback ::The state of the context changed"); + _info("Audio: The state of the context changed"); PulseLayer* pulse = (PulseLayer*) user_data; assert (c && pulse->m); @@ -195,46 +208,45 @@ void PulseLayer::context_state_callback (pa_context* c, void* user_data) case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: - _debug ("Waiting...."); + _debug ("Audio: Waiting...."); break; case PA_CONTEXT_READY: - pulse->createStreams (c); - _debug ("Connection to PulseAudio server established"); + _debug ("Audio: Connection to PulseAudio server established"); + pa_threaded_mainloop_signal(pulse->m, 0); break; case PA_CONTEXT_TERMINATED: - _debug ("Context terminated"); + _debug ("Audio: Context terminated"); break; case PA_CONTEXT_FAILED: default: - _debug (" Error : %s" , pa_strerror (pa_context_errno (c))); + _warn("Audio: Error : %s" , pa_strerror (pa_context_errno (c))); pulse->disconnectAudioStream(); exit (0); break; } } -bool PulseLayer::disconnectAudioStream (void) +bool PulseLayer::openDevice (int indexIn UNUSED, int indexOut UNUSED, int sampleRate, int frameSize , int stream UNUSED, std::string plugin UNUSED) { - _debug (" PulseLayer::disconnectAudioStream( void ) "); + _audioSampleRate = sampleRate; + _frameSize = frameSize; - closePlaybackStream(); + flushUrgent(); - closeCaptureStream(); + // use 1 sec buffer for resampling + _converter = new SamplerateConverter (_audioSampleRate, 1000); - if (!playback && !record) - return true; - else - return false; + return true; } bool PulseLayer::createStreams (pa_context* c) { - _debug ("PulseLayer::createStreams"); + _info("Audio: Create streams"); PulseLayerType * playbackParam = new PulseLayerType(); playbackParam->context = c; @@ -270,27 +282,26 @@ bool PulseLayer::createStreams (pa_context* c) flushMain(); flushUrgent(); - // _urgentRingBuffer.flushAll(); - return true; } -bool PulseLayer::openDevice (int indexIn UNUSED, int indexOut UNUSED, int sampleRate, int frameSize , int stream UNUSED, std::string plugin UNUSED) +bool PulseLayer::disconnectAudioStream (void) { - _audioSampleRate = sampleRate; - _frameSize = frameSize; + _info("Audio: Disconnect audio stream"); - // _urgentRingBuffer.flushAll(); - flushUrgent(); + closePlaybackStream(); - // use 1 sec buffer for resampling - _converter = new SamplerateConverter (_audioSampleRate, 1000); + closeCaptureStream(); - return true; + if (!playback && !record) + return true; + else + return false; } + void PulseLayer::closeCaptureStream (void) { if (record) { @@ -300,6 +311,7 @@ void PulseLayer::closeCaptureStream (void) } } + void PulseLayer::closePlaybackStream (void) { if (playback) { @@ -309,60 +321,30 @@ void PulseLayer::closePlaybackStream (void) } } + int PulseLayer::canGetMic() { if (record) - return 0; // _micRingBuffer.AvailForGet(); + return 0; else return 0; } + int PulseLayer::getMic (void *buffer, int toCopy) { if (record) { - return 0; // _micRingBuffer.Get (buffer, toCopy, 100); + return 0; } else return 0; } + void PulseLayer::startStream (void) { - // connectPulseAudioServer(); - - if (!is_started) { - - _debug ("PulseLayer::Start Stream"); - - if (!m) { - - _debug ("Creating PulseAudio MainLoop"); - m = pa_threaded_mainloop_new(); - assert (m); - - if (pa_threaded_mainloop_start (m) < 0) { - _debug ("Failed starting the mainloop"); - } - } - - if (!context) { - - _debug ("Creating new PulseAudio Context"); - pa_threaded_mainloop_lock (m); - // Instanciate a context - - if (! (context = pa_context_new (pa_threaded_mainloop_get_api (m) , "SFLphone"))) - _debug ("Error while creating the context"); - - pa_threaded_mainloop_unlock (m); - - assert (context); - } - - // Create Streams - connectPulseAudioServer(); - - is_started = true; - } + // Create Streams + if(!playback || !record) + createStreams(context); // Flush outside the if statement: every time start stream is // called is to notify a new event @@ -372,71 +354,25 @@ void PulseLayer::startStream (void) } + void PulseLayer::stopStream (void) { - if (is_started) { - - _debug ("PulseLayer::Stop Audio Stream"); - pa_stream_flush (playback->pulseStream(), NULL, NULL); - pa_stream_flush (record->pulseStream(), NULL, NULL); - - if (m) { - pa_threaded_mainloop_stop (m); - } - - disconnectAudioStream(); - - _debug ("Disconnecting PulseAudio context"); - - if (context) { - - pa_threaded_mainloop_lock (m); - pa_context_disconnect (context); - pa_context_unref (context); - pa_threaded_mainloop_unlock (m); - context = NULL; - } - - _debug ("Freeing Pulseaudio mainloop"); - - if (m) { - pa_threaded_mainloop_free (m); - m = NULL; - } - - - is_started = false; - - } + _info("Audio: Stop audio stream"); + pa_stream_flush (playback->pulseStream(), NULL, NULL); + pa_stream_flush (record->pulseStream(), NULL, NULL); + disconnectAudioStream(); } -// void PulseLayer::underflow (pa_stream* s UNUSED, void* userdata UNUSED) -//{ -// _debug ("PulseLayer::Buffer Underflow"); -//} - -/* -void PulseLayer::overflow (pa_stream* s, void* userdata UNUSED) -{ - //PulseLayer* pulse = (PulseLayer*) userdata; - pa_stream_drop (s); - pa_stream_trigger (s, NULL, NULL); -} -*/ - - void PulseLayer::processPlaybackData (void) { // Handle the data for the speakers if (playback && (playback->pulseStream()) && (pa_stream_get_state (playback->pulseStream()) == PA_STREAM_READY)) { - // _debug("PulseLayer::processPlaybackData()"); - // If the playback buffer is full, we don't overflow it; wait for it to have free space if (pa_stream_writable_size (playback->pulseStream()) == 0) return; @@ -464,8 +400,6 @@ void PulseLayer::processData (void) // Handle the data for the speakers if (playback && (playback->pulseStream()) && (pa_stream_get_state (playback->pulseStream()) == PA_STREAM_READY)) { - // _debug("PulseLayer::processPlaybackData()"); - // If the playback buffer is full, we don't overflow it; wait for it to have free space if (pa_stream_writable_size (playback->pulseStream()) == 0) return; @@ -499,7 +433,7 @@ void PulseLayer::writeToSpeaker (void) int writeableSize = pa_stream_writable_size (playback->pulseStream()); if (writeableSize < 0) - _debug ("PulseLayer playback error : %s", pa_strerror (writeableSize)); + _warn ("Audio: playback error : %s", pa_strerror (writeableSize)); if (urgentAvailBytes > writeableSize) { @@ -527,15 +461,11 @@ void PulseLayer::writeToSpeaker (void) if (playback->getStreamState() == PA_STREAM_READY) { - // _debug("writeableSize %d\n", writeableSize); - out = (SFLDataFormat*) pa_xmalloc (writeableSize); int copied = tone->getNext (out, writeableSize / sizeof (SFLDataFormat), 100); int returnValue = pa_stream_write (playback->pulseStream(), out, copied * sizeof (SFLDataFormat), NULL, 0, PA_SEEK_RELATIVE); - // _debug("return value %d\n", returnValue); - pa_xfree (out); } @@ -580,7 +510,6 @@ void PulseLayer::writeToSpeaker (void) normalAvailBytes = getMainBuffer()->availForGet(); byteToGet = (normalAvailBytes < (int) (maxNbBytesToGet)) ? normalAvailBytes : maxNbBytesToGet; - // _debug("byteToGet: %i", byteToGet); if (byteToGet) { @@ -604,12 +533,10 @@ void PulseLayer::writeToSpeaker (void) int nbSample = _converter->upsampleData ( (SFLDataFormat*) out, rsmpl_out, _mainBufferSampleRate, _audioSampleRate, nb_sample_down); if ( (nbSample*sizeof (SFLDataFormat)) > (unsigned int) writeableSize) - _debug ("Error: nbsbyte exceed buffer length"); + _warn("Audio: Error: nbsbyte exceed buffer length"); - // pa_threaded_mainloop_lock (m); pa_stream_write (playback->pulseStream(), rsmpl_out, nbSample*sizeof (SFLDataFormat), NULL, 0, PA_SEEK_RELATIVE); - // pa_threaded_mainloop_unlock (m); pa_xfree (rsmpl_out); } else { @@ -628,9 +555,7 @@ void PulseLayer::writeToSpeaker (void) bzero (zeros, writeableSize); - // pa_threaded_mainloop_lock (m); pa_stream_write (playback->pulseStream(), zeros, writeableSize, NULL, 0, PA_SEEK_RELATIVE); - // pa_threaded_mainloop_unlock (m); pa_xfree (zeros); @@ -651,14 +576,11 @@ void PulseLayer::readFromMic (void) const char* data = NULL; size_t r; - // if (record->getStreamState() - int readableSize = pa_stream_readable_size (record->pulseStream()); - // _debug("readableSize: %i", readableSize); if (pa_stream_peek (record->pulseStream() , (const void**) &data , &r) < 0 || !data) { - _debug ("pa_stream_peek() failed: %s" , pa_strerror (pa_context_errno (context))); + _warn("Audio: Error capture stream peek failed: %s" , pa_strerror (pa_context_errno (context))); } @@ -667,13 +589,10 @@ void PulseLayer::readFromMic (void) int _mainBufferSampleRate = getMainBuffer()->getInternalSamplingRate(); // test if resampling is required - if (_mainBufferSampleRate && ( (int) _audioSampleRate != _mainBufferSampleRate)) { - - SFLDataFormat* rsmpl_out = (SFLDataFormat*) pa_xmalloc (readableSize); - // _debug("Byte read: %i", r); + int nbSample = r / sizeof (SFLDataFormat); int nb_sample_up = nbSample; @@ -699,10 +618,9 @@ void PulseLayer::readFromMic (void) } if (pa_stream_drop (record->pulseStream()) < 0) { - //_debug("pa_stream_drop() failed: %s" , pa_strerror( pa_context_errno( context) )); + _warn("Audio: Error: capture stream drop failed: %s" , pa_strerror( pa_context_errno( context) )); } - // pa_threaded_mainloop_unlock (m); } static void retrieve_server_info (pa_context *c UNUSED, const pa_server_info *i, void *userdata UNUSED) diff --git a/sflphone-common/src/audio/pulseaudio/pulselayer.h b/sflphone-common/src/audio/pulseaudio/pulselayer.h index eb252408313c1873f47a1a688df800471ebe7942..a8248b56be1ee1e1b543ce6f96efea0b88f51c31 100644 --- a/sflphone-common/src/audio/pulseaudio/pulselayer.h +++ b/sflphone-common/src/audio/pulseaudio/pulselayer.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2008 Savoir-Faire Linux inc. * Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com> + * Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +42,8 @@ class PulseLayer : public AudioLayer { PulseLayer(ManagerImpl* manager); ~PulseLayer(void); + void openLayer( void ); + bool closeLayer( void ); /**