View | Details | Raw Unified
Collapse All | Expand All

(-) last.fm-1.1.3.orig/LastFM.pro (-1 / +12 lines)
 Lines 5-12    Link Here 
           src/webservice/ src/settingsservice/ \
           src/webservice/ src/settingsservice/ \
           src/httpinput/ \
           src/httpinput/ \
           src/mp3transcode/ \
           src/mp3transcode/ \
           src/rtaudioplayback/ \
           src/metadataextension/ src/sidebarextension/ src/searchextension/ src/userinfoextension/ \
           src/metadataextension/ src/sidebarextension/ src/searchextension/ src/userinfoextension/ \
           src/mediadevices/itunes/
           src/mediadevices/itunes/
TRANSLATIONS = i18n/lastfm_jp_JP.ts
TRANSLATIONS = i18n/lastfm_jp_JP.ts
win32 {
    SUBDIRS += src/rtaudioplayback/
}
unix:!linux-g++ {
    SUBDIRS += src/rtaudioplayback/
}
unix:linux-g++ {
    SUBDIRS += src/alsaplayback/
}
(-) last.fm-1.1.3.orig/src/alsaplayback/alsaaudio.cpp (+851 lines)
Line 0    Link Here 
/***************************************************************************
 *   Copyright (C) 2007 by John Stamp                                      *
 *   jstamp@users.sourceforge.net                                          *
 *                                                                         *
 *   Large portions of this code are shamelessly copied from audio.c:      *
 *   The XMMS ALSA output plugin                                           *
 *   Copyright (C) 2001-2003 Matthieu Sozeau <mattam@altern.org>           *
 *   Copyright (C) 1998-2003  Peter Alm, Mikael Alm, Olle Hallnas,         *
 *                            Thomas Nilsson and 4Front Technologies       *
 *   Copyright (C) 1999-2006  Haavard Kvaalen                              *
 *   Copyright (C) 2005       Takashi Iwai                                 *
 *                                                                         *
 *   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  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "alsaaudio.h"
#include "Loqqer.h"
#include <qendian.h>
#include <QDebug>
#define MIN( a, b )  ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )
#define MAX_BUFFER_SIZE 5
QMutex AlsaAudio::mutex;
pthread_t AlsaAudio::audio_thread;
QByteArray AlsaAudio::audioData;
snd_output_t* AlsaAudio::logs = NULL;
bool AlsaAudio::going = false;
snd_pcm_t *AlsaAudio::alsa_pcm = NULL;
int AlsaAudio::hw_period_size_in = 0;
snd_format* AlsaAudio::inputf = NULL;
snd_format* AlsaAudio::outputf = NULL;
float AlsaAudio::volume = 1.0;
convert_func_t         AlsaAudio::alsa_convert_func = NULL;
convert_channel_func_t AlsaAudio::alsa_stereo_convert_func =NULL;
convert_freq_func_t    AlsaAudio::alsa_frequency_convert_func =NULL;
xmms_convert_buffers   *AlsaAudio::convertb = NULL;
bool AlsaAudio::use_mmap = false;
AlsaAudio::AlsaAudio()
{
    maxBufferSize = 0;
}
AlsaAudio::~AlsaAudio()
{
}
/******************************************************************************
    Device Detection
******************************************************************************/
int AlsaAudio::getCards( void )
{
    int card = -1;
    int err = 0;
    _devices.clear();
    if ( ( err = snd_card_next( &card ) ) != 0 )
    {
        LOGL( 1, "AlsaAudio::getCards() failed: " << snd_strerror( -err ) );
        return -1;
    }
    while ( card > -1 )
    {
        getDevicesForCard( card );
        if ( ( err = snd_card_next( &card ) ) != 0 )
        {
            LOGL( 1, "AlsaAudio::getCards() failed: " << snd_strerror( -err ) );
            return -1;
        }
    }
    return _devices.size();
}
void AlsaAudio::getDevicesForCard( int card )
{
    int pcm_device = -1, err;
    snd_pcm_info_t *pcm_info;
    snd_ctl_t *ctl;
    char devName[64], *card_name;
    sprintf( devName, "hw:%i", card );
    if ( ( err = snd_ctl_open( &ctl, devName, 0 ) ) < 0 )
    {
        LOGL( 1, "AlsaAudio::getDevicesForCard() failed: " << snd_strerror( -err ) );
        return;
    }
    if ( ( err = snd_card_get_name( card, &card_name ) ) != 0 )
    {
        LOGL( 1, "AlsaAudio::getDevicesForCard() failed: " << snd_strerror( -err ) );
        card_name = "Unknown soundcard";
    }
    // Each card has its own default device
    // But test, just to be sure it's there
    AlsaDeviceInfo dev;
    dev.name = QString( "%1: Default Device (default:%2)" ).arg( card_name ).arg( card );
    dev.device = QString( "default:%1" ).arg( card );
    snd_pcm_t *test_pcm;
    err = snd_pcm_open( &test_pcm, dev.device.toStdString().c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK );
    if ( err >= 0 )
        snd_pcm_close( test_pcm );
    if ( err == 0 || err == -EBUSY )
        _devices.push_back( dev );
    snd_pcm_info_alloca( &pcm_info );
    for ( ;; )
    {
        char device[64], descr[128];
        if ( ( err = snd_ctl_pcm_next_device( ctl, &pcm_device ) ) < 0 )
        {
            LOGL( 1, "AlsaAudio::getDevicesForCard() failed: " << snd_strerror( -err ) );
            pcm_device = -1;
        }
        if ( pcm_device < 0 )
            break;
        snd_pcm_info_set_device( pcm_info, pcm_device );
        snd_pcm_info_set_subdevice( pcm_info, 0 );
        snd_pcm_info_set_stream( pcm_info, SND_PCM_STREAM_PLAYBACK );
        if ( ( err = snd_ctl_pcm_info( ctl, pcm_info ) ) < 0 )
        {
            if ( err != -ENOENT )
                LOGL( 1, "AlsaAudio::getDevicesForCard: snd_ctl_pcm_info() failed ("
                         << card << ":" << pcm_device << "): " << snd_strerror( -err ) );
            continue;
        }
        sprintf( device, "hw:%d,%d", card, pcm_device );
        sprintf( descr, "%s: %s (%s)", card_name,
                                       snd_pcm_info_get_name( pcm_info ),
                                       device );
        dev.name = descr;
        dev.device = device;
        _devices.push_back( dev );
    }
    snd_ctl_close( ctl );
}
AlsaDeviceInfo AlsaAudio::getDeviceInfo( int device )
{
    return _devices[device];
}
/******************************************************************************
    Device Setup
******************************************************************************/
bool AlsaAudio::alsaSetup( QString device, snd_pcm_uframes_t periodSize, uint periodCount, snd_format *f )
{
    int err, hw_period_size;
    snd_pcm_hw_params_t *hwparams;
    snd_pcm_sw_params_t *swparams;
    snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
    snd_pcm_access_mask_t *mask;
#ifndef QT_NO_DEBUG
    qDebug() << "AlsaAudio::alsaSetup()";
    snd_output_stdio_attach( &logs, stderr, 0 );
#endif
    alsa_convert_func = NULL;
    alsa_stereo_convert_func = NULL;
    alsa_frequency_convert_func = NULL;
    free( outputf );
    outputf = snd_format_from_xmms( f->xmms_format, f->rate, f->channels );
#ifndef QT_NO_DEBUG
    qDebug() << "Opening device:" << device;
#endif
    // FIXME: Can snd_pcm_open() return EAGAIN?
    if ( ( err = snd_pcm_open( &alsa_pcm, device.toStdString().c_str(),
                 SND_PCM_STREAM_PLAYBACK,
                 SND_PCM_NONBLOCK ) ) < 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup(): Failed to open pcm device (" << device << "): " << snd_strerror( -err ) );
        alsa_pcm = NULL;
        free( outputf );
        outputf = NULL;
        return false;
    }
#ifndef QT_NO_DEBUG
    snd_pcm_info_t *info;
    int alsa_card, alsa_device, alsa_subdevice;
    snd_pcm_info_alloca( &info );
    snd_pcm_info( alsa_pcm, info );
    alsa_card = snd_pcm_info_get_card( info );
    alsa_device = snd_pcm_info_get_device( info );
    alsa_subdevice = snd_pcm_info_get_subdevice( info );
    qDebug() << "Card:" << alsa_card << "Device:" << alsa_device << "Subdevice:" << alsa_subdevice;
#endif
    snd_pcm_hw_params_alloca( &hwparams );
    if ( ( err = snd_pcm_hw_params_any( alsa_pcm, hwparams ) ) < 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup(): No configuration available for playback: " << snd_strerror( -err ) );
        return false;
    }
    // First try to set up mmapped access
    mask = (snd_pcm_access_mask_t*)alloca(snd_pcm_access_mask_sizeof());
    snd_pcm_access_mask_none(mask);
    snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
    snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
    snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
#ifndef QT_NO_DEBUG
    qDebug() << "Trying to set mmapped write mode";
#endif
    if ( ( err = snd_pcm_hw_params_set_access_mask( alsa_pcm, hwparams, mask ) ) < 0 )
    {
        use_mmap = false;
#ifndef QT_NO_DEBUG
        qDebug() << "Setting mmapped write mode failed: " << snd_strerror( -err ) << "\n Trying normal write mode";
#endif
        if ( ( err = snd_pcm_hw_params_set_access( alsa_pcm, hwparams,
               SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
        {
            LOGL( 1, "AlsaAudio::alsaSetup(): Cannot set normal write mode: " << snd_strerror( -err ) );
            return false;
        }
    }
    else
        use_mmap = true;
    if ( ( err = snd_pcm_hw_params_set_format( alsa_pcm, hwparams, outputf->format ) ) < 0 )
    {
        //Try if one of these format work (one of them should work
        //on almost all soundcards)
        snd_pcm_format_t formats[] = { SND_PCM_FORMAT_S16_LE,
                                       SND_PCM_FORMAT_S16_BE,
                                       SND_PCM_FORMAT_U8
                                     };
        uint i;
        for ( i = 0; i < sizeof( formats ) / sizeof( formats[0] ); i++ )
        {
            if ( snd_pcm_hw_params_set_format( alsa_pcm, hwparams, formats[i] ) == 0 )
            {
                outputf->format = formats[i];
                break;
            }
        }
        if ( outputf->format != f->format )
        {
            outputf->xmms_format = (AFormat)format_from_alsa( outputf->format );
#ifndef QT_NO_DEBUG
            qDebug() << "Converting format from" << f->xmms_format << "to" << outputf->xmms_format;
#endif
            if ( outputf->xmms_format < 0 )
                return -1;
            alsa_convert_func = xmms_convert_get_func( outputf->xmms_format, f->xmms_format );
            if ( alsa_convert_func == NULL )
            {
                LOGL( 1, "Format translation needed, but not available.  Input: " << f->xmms_format << "; Output: " << outputf->xmms_format );
                return false;
            }
        }
        else
        {
            LOGL( 1, "AlsaAudio::alsaSetup(): Sample format not available for playback: " << snd_strerror( -err ) );
            return false;
        }
    }
    snd_pcm_hw_params_set_channels_near( alsa_pcm, hwparams, &outputf->channels );
    if ( outputf->channels != f->channels )
    {
#ifndef QT_NO_DEBUG
        qDebug() << "Converting channels from" << f->channels << "to" << outputf->channels;
#endif
        alsa_stereo_convert_func =
                xmms_convert_get_channel_func( outputf->xmms_format,
                                               outputf->channels,
                                               f->channels );
        if ( alsa_stereo_convert_func == NULL )
        {
            LOGL( 1, "No stereo conversion available.  Format: " << outputf->xmms_format << "; Input Channels: " << f->channels << "; Output Channels: " << outputf->channels );
            return false;
        }
    }
    snd_pcm_hw_params_set_rate_near( alsa_pcm, hwparams, &outputf->rate, 0 );
    if ( outputf->rate == 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup: No usable samplerate available." );
        return false;
    }
    if ( outputf->rate != f->rate )
    {
        LOGL( 3, "Converting samplerate from " << f->rate << "  to " << outputf->rate );
        if ( outputf->channels < 1 || outputf->channels > 2 )
        {
            LOGL( 1, "Unsupported number of channels: " << outputf->channels << ". Resample function not available" );
            alsa_frequency_convert_func = NULL;
            return false;
        }
        alsa_frequency_convert_func =
                xmms_convert_get_frequency_func( outputf->xmms_format,
                                                 outputf->channels );
        if ( alsa_frequency_convert_func == NULL )
        {
            LOGL( 1, "Resample function not available.  Format " << outputf->xmms_format );
            return false;
        }
    }
    outputf->sample_bits = snd_pcm_format_physical_width( outputf->format );
    outputf->bps = ( outputf->rate * outputf->sample_bits * outputf->channels ) >> 3;
    if ( ( err = snd_pcm_hw_params_set_period_size_near( alsa_pcm, hwparams,
                                                         &periodSize, NULL ) ) < 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup(): Set period size failed: " << snd_strerror( -err ) );
        return false;
    }
    if ( ( err = snd_pcm_hw_params_set_periods_near( alsa_pcm, hwparams,
         &periodCount, 0 ) ) < 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup(): Set period count failed: " << snd_strerror( -err ) );
        return false;
    }
    if ( snd_pcm_hw_params( alsa_pcm, hwparams ) < 0 )
    {
#ifndef QT_NO_DEBUG
        snd_pcm_hw_params_dump( hwparams, logs );
#endif
        LOGL( 1, "AlsaAudio::alsaSetup(): Unable to install hw params" );
        return false;
    }
    if ( ( err = snd_pcm_hw_params_get_buffer_size( hwparams, &alsa_buffer_size ) ) < 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup(): snd_pcm_hw_params_get_buffer_size() failed: " << snd_strerror( -err ) );
        return false;
    }
    if ( ( err = snd_pcm_hw_params_get_period_size( hwparams, &alsa_period_size, 0 ) ) < 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup(): snd_pcm_hw_params_get_period_size() failed: " << snd_strerror( -err ) );
        return false;
    }
    snd_pcm_sw_params_alloca( &swparams );
    snd_pcm_sw_params_current( alsa_pcm, swparams );
    if ( ( err = snd_pcm_sw_params_set_start_threshold( alsa_pcm,
         swparams, alsa_buffer_size - alsa_period_size ) < 0 ) )
        LOGL( 1, "AlsaAudio::alsaSetup(): setting start threshold failed: " << snd_strerror( -err ) );
    if ( snd_pcm_sw_params( alsa_pcm, swparams ) < 0 )
    {
        LOGL( 1, "AlsaAudio::alsaSetup(): Unable to install sw params" );
        return false;
    }
#ifndef QT_NO_DEBUG
    snd_pcm_sw_params_dump( swparams, logs );
    snd_pcm_dump( alsa_pcm, logs );
#endif
    hw_period_size = snd_pcm_frames_to_bytes( alsa_pcm, alsa_period_size );
    if ( inputf->bps != outputf->bps )
    {
        int align = ( inputf->sample_bits * inputf->channels ) / 8;
        hw_period_size_in = ( (quint64)hw_period_size * inputf->bps +
                            outputf->bps/2 ) / outputf->bps;
        hw_period_size_in -= hw_period_size_in % align;
    }
    else
    {
        hw_period_size_in = hw_period_size;
    }
#ifndef QT_NO_DEBUG
    qDebug() << "Device setup: period size:" << hw_period_size;
    qDebug() << "bits per sample:" <<  snd_pcm_format_physical_width( outputf->format ) <<
            "frame size:" <<  snd_pcm_frames_to_bytes( alsa_pcm, 1 ) <<
            "Bps:" << outputf->bps;
#endif
    return true;
}
bool AlsaAudio::alsaOpen( QString device, AFormat format, unsigned int rate, unsigned int channels, unsigned int buffer_time, unsigned int period_time )
{
#ifndef QT_NO_DEBUG
    qDebug() << "Opening device";
#endif
    inputf = snd_format_from_xmms( format, rate, channels );
    // We'll be using this in alsaWrite
    maxBufferSize = inputf->bps * MAX_BUFFER_SIZE;
    // And clear the buffer, just in case
    clearBuffer();
    if ( alsaSetup( device, buffer_time, period_time, inputf ) == false )
    {
        alsaClose();
        return false;
    }
    going = true;
    convertb = xmms_convert_buffers_new();
    AlsaAudio* aaThread = new AlsaAudio();
#ifndef QT_NO_DEBUG
    qDebug() << "Starting thread";
#endif
    pthread_create( &audio_thread, NULL, &alsa_loop, (void*)aaThread );
    return true;
}
void AlsaAudio::clearBuffer( void )
{
    mutex.lock();
    audioData.clear();
    mutex.unlock();
}
/******************************************************************************
    Play Interface
******************************************************************************/
void AlsaAudio::alsaWrite( const QByteArray* inputData )
{
    if ( ( audioData.size() + inputData->size() ) < maxBufferSize )
    {
        mutex.lock();
#ifndef QT_NO_DEBUG
    qDebug( "max buffer size: %d; buffer data: %d; input data: %d", maxBufferSize, audioData.size(), inputData->size() );
#endif
        audioData.append( *inputData );
        mutex.unlock();
    }
    else
    {
        mutex.lock();
        int inSize = maxBufferSize - audioData.size();
#ifndef QT_NO_DEBUG
    qDebug( "max buffer size: %d; buffer data: %d; input data: %d; truncated to: %d", 
            maxBufferSize, audioData.size(), inputData->size(), inSize );
#endif
        audioData.append( inputData->left( inSize ) );
        mutex.unlock();
        LOGL( 1, "Max data buffer size reached.  Bytes dropped: " << inSize );
    }
}
int AlsaAudio::bufferSize( void )
{
    return audioData.size();
}
void AlsaAudio::setVolume ( float vol )
{
    volume = vol;
}
void AlsaAudio::alsaClose( void )
{
    if ( !going )
        return;
#ifndef QT_NO_DEBUG
    qDebug() << "Closing device";
#endif
    going = false;
    pthread_join( audio_thread, NULL );
    xmms_convert_buffers_destroy( convertb );
    convertb = NULL;
    free( inputf );
    inputf = NULL;
    free( outputf );
    outputf = NULL;
#ifndef QT_NO_DEBUG
    snd_output_close( logs );
    qDebug() << "Device closed";
#endif
}
/******************************************************************************
    Play Thread
******************************************************************************/
void* AlsaAudio::alsa_loop( void* pthis )
{
  AlsaAudio* aaThread = (AlsaAudio*)pthis;
  aaThread->run();
  return NULL;
}
void AlsaAudio::run( void )
{
    int npfds = snd_pcm_poll_descriptors_count( alsa_pcm );
    struct pollfd *pfds;
    unsigned short *revents;
    if ( npfds <= 0 )
        goto _error;
    pfds = (struct pollfd*)malloc( sizeof( *pfds ) * npfds );
    revents = (unsigned short*)malloc( sizeof( *revents ) * npfds );
    while ( going && alsa_pcm )
    {
        if ( audioData.size() > hw_period_size_in )
        {
            snd_pcm_poll_descriptors( alsa_pcm, pfds, npfds );
            if ( poll( pfds, npfds, 10 ) > 0 )
            {
                // need to check revents.  poll() with
                // dmix returns a postive value even
                // if no data is available
                int i;
                snd_pcm_poll_descriptors_revents( alsa_pcm, pfds,
                        npfds, revents );
                for ( i = 0; i < npfds; i++ )
                    if ( revents[i] & POLLOUT )
                {
                    pumpThreadData();
                    break;
                }
            }
        }
        else
        {
            struct timespec req;
            req.tv_sec = 0;
            req.tv_nsec = 10000000;
            nanosleep( &req, NULL );
        }
    }
    free( pfds );
    free( revents );
 _error:
    alsa_close_pcm();
    mutex.lock();
    audioData.clear();
    mutex.unlock();
#ifndef QT_NO_DEBUG
    qDebug() << "Exiting thread";
#endif
    pthread_exit( NULL );
}
/* transfer audio data from thread buffer to h/w */
void AlsaAudio::pumpThreadData( void )
{
    int length, cnt, avail, datSize;
    datSize = audioData.size();
    length = MIN( hw_period_size_in, datSize );
    avail = snd_pcm_frames_to_bytes( alsa_pcm, getAvailableFrames() );
    length = MIN( length, avail );
    while ( length > 0 )
    {
        cnt = MIN( length, datSize );
        convertData( audioData.left( cnt ).data(), cnt );
        mutex.lock();
        audioData.remove( 0, cnt );
        mutex.unlock();
        length -= cnt;
    }
}
/* update and get the available space on h/w buffer (in frames) */
snd_pcm_sframes_t AlsaAudio::getAvailableFrames( void )
{
    snd_pcm_sframes_t ret;
    if ( alsa_pcm == NULL )
        return 0;
    while ( ( ret = snd_pcm_avail_update( alsa_pcm ) ) < 0 )
    {
        ret = alsa_handle_error( ret );
        if ( ret < 0 )
        {
            LOGL( 1, "alsa_get_avail(): snd_pcm_avail_update() failed: " << snd_strerror( -ret ) );
            return 0;
        }
        return 0;
    }
    return ret;
}
/* transfer data to audio h/w; length is given in bytes
 *
 * data can be modified via rate conversion or
 * software volume before passed to audio h/w
 */
void AlsaAudio::convertData( void* data, int length )
{
    if ( alsa_convert_func != NULL )
        length = alsa_convert_func( convertb, &data, length );
    if ( alsa_stereo_convert_func != NULL )
        length = alsa_stereo_convert_func( convertb, &data, length );
    if ( alsa_frequency_convert_func != NULL )
    {
        length = alsa_frequency_convert_func( convertb, &data, length,
                                              inputf->rate,
                                              outputf->rate );
    }
    adjustVolume( data, length, outputf->xmms_format );
    writeToCard( (char*)data, length );
}
#define VOLUME_ADJUST( type, endian )                                       \
do {                                                                        \
    type *ptr = (type*)data;                                                \
    for ( i = 0; i < length; i += 2 )                                       \
    {                                                                       \
        *ptr = qTo##endian( (type)( qFrom##endian( *ptr ) * volume  ) );    \
        ptr++;                                                              \
    }                                                                       \
} while ( 0 )
#define VOLUME_ADJUST8( type )           \
do {                                     \
    type *ptr = (type*)data;             \
    for ( i = 0; i < length; i++ )       \
    {                                    \
        *ptr = (type)( *ptr * volume );  \
        ptr++;                           \
    }                                    \
} while ( 0 )
void AlsaAudio::adjustVolume( void* data, int length, AFormat fmt )
{
    int i;
    if ( volume == 1.0 )
        return;
    switch ( fmt )
    {
        case FMT_S16_LE:
            VOLUME_ADJUST( qint16, LittleEndian );
            break;
        case FMT_U16_LE:
            VOLUME_ADJUST( quint16, LittleEndian );
            break;
        case FMT_S16_BE:
            VOLUME_ADJUST( qint16, BigEndian );
            break;
        case FMT_U16_BE:
            VOLUME_ADJUST( quint16, BigEndian );
            break;
        case FMT_S8:
            VOLUME_ADJUST8( qint8 );
            break;
        case FMT_U8:
            VOLUME_ADJUST8( quint8 );
            break;
        default:
            LOGL( 1, "AlsaAudio::adjustVolume(): unhandled format: " << fmt );
            break;
    }
}
/* transfer data to audio h/w via normal write */
void AlsaAudio::writeToCard( char *data, int length )
{
    snd_pcm_sframes_t written_frames;
    while ( length > 0 )
    {
        int frames = snd_pcm_bytes_to_frames( alsa_pcm, length );
        if ( use_mmap )
        {
            written_frames = snd_pcm_mmap_writei( alsa_pcm, data, frames );
        }
        else
            written_frames = snd_pcm_writei( alsa_pcm, data, frames );
        if ( written_frames > 0 )
        {
            int written = snd_pcm_frames_to_bytes( alsa_pcm, written_frames );
            length -= written;
            data += written;
        }
        else
        {
            int err = alsa_handle_error( (int)written_frames );
            if ( err < 0 )
            {
                LOGL( 1, "AlsaAudio::writeToCard(): write error: " << snd_strerror( -err ) );
                break;
            }
        }
    }
}
/* handle generic errors */
int AlsaAudio::alsa_handle_error( int err )
{
    switch ( err )
    {
        case -EPIPE:
            return xrun_recover();
        case -ESTRPIPE:
            return suspend_recover();
    }
    return err;
}
/* close PCM and release associated resources */
void AlsaAudio::alsa_close_pcm( void )
{
    if ( alsa_pcm )
    {
        int err;
        snd_pcm_drop( alsa_pcm );
        if ( ( err = snd_pcm_close( alsa_pcm ) ) < 0 )
            LOGL( 1, "alsa_pcm_close() failed: " << snd_strerror( -err ) );
        alsa_pcm = NULL;
    }
}
int AlsaAudio::format_from_alsa( snd_pcm_format_t fmt )
{
    uint i;
    for ( i = 0; i < sizeof( format_table ) / sizeof( format_table[0] ); i++ )
        if ( format_table[i].alsa == fmt )
            return format_table[i].xmms;
    LOGL( 1, "Unsupported format: " << snd_pcm_format_name( fmt ) );
    return -1;
}
struct snd_format * AlsaAudio::snd_format_from_xmms( AFormat fmt, int rate, int channels )
{
    struct snd_format *f = (struct snd_format*)malloc( sizeof( struct snd_format ) );
    uint i;
    f->xmms_format = fmt;
    f->format = SND_PCM_FORMAT_UNKNOWN;
    for ( i = 0; i < sizeof( format_table ) / sizeof( format_table[0] ); i++ )
    {
        if ( format_table[i].xmms == fmt )
        {
            f->format = format_table[i].alsa;
            break;
        }
    }
    /* Get rid of _NE */
    for ( i = 0; i < sizeof( format_table ) / sizeof( format_table[0] ); i++ )
    {
        if ( format_table[i].alsa == f->format )
        {
            f->xmms_format = format_table[i].xmms;
            break;
        }
    }
    f->rate = rate;
    f->channels = channels;
    f->sample_bits = snd_pcm_format_physical_width( f->format );
    f->bps = ( rate * f->sample_bits * channels ) >> 3;
    return f;
}
int AlsaAudio::xrun_recover( void )
{
#ifndef QT_NO_DEBUG
    snd_pcm_status_t *alsa_status;
    snd_pcm_status_alloca( &alsa_status );
    if ( snd_pcm_status( alsa_pcm, alsa_status ) < 0 )
    {
        qDebug() << "AlsaAudio::xrun_recover(): snd_pcm_status() failed";
    }
    else
    {
        snd_pcm_status_dump( alsa_status, logs );
        qDebug() << "Status:\n" << logs;
    }
#endif
    return snd_pcm_prepare( alsa_pcm );
}
int AlsaAudio::suspend_recover( void )
{
    int err;
    while ( ( err = snd_pcm_resume( alsa_pcm ) ) == -EAGAIN )
        /* wait until suspend flag is released */
        sleep( 1 );
    if ( err < 0 )
    {
        LOGL( 3, "alsa_handle_error(): snd_pcm_resume() failed." );
        return snd_pcm_prepare( alsa_pcm );
    }
    return err;
}
(-) last.fm-1.1.3.orig/src/alsaplayback/alsaaudio.h (+121 lines)
Line 0    Link Here 
/***************************************************************************
 *   Copyright (C) 2007 by John Stamp                                      *
 *   jstamp@users.sourceforge.net                                          *
 *                                                                         *
 *   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  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef ALSAAUDIO_H
#define ALSAAUDIO_H
#include <alsa/asoundlib.h>
#include <QByteArray>
#include <QList>
#include <QString>
#include <QMutex>
#include "xconvert.h"
struct AlsaDeviceInfo {
    QString name;
    QString device;
};
struct snd_format {
    unsigned int rate;
    unsigned int channels;
    snd_pcm_format_t format;
    AFormat xmms_format;
    int sample_bits;
    int bps;
};
static const struct {
    AFormat xmms;
    snd_pcm_format_t alsa;
} format_table[] =
                   { { FMT_S16_LE, SND_PCM_FORMAT_S16_LE },
                     { FMT_S16_BE, SND_PCM_FORMAT_S16_BE },
                     { FMT_S16_NE, SND_PCM_FORMAT_S16    },
                     { FMT_U16_LE, SND_PCM_FORMAT_U16_LE },
                     { FMT_U16_BE, SND_PCM_FORMAT_U16_BE },
                     { FMT_U16_NE, SND_PCM_FORMAT_U16    },
                     { FMT_U8,     SND_PCM_FORMAT_U8     },
                     { FMT_S8,     SND_PCM_FORMAT_S8     },
                   };
class AlsaAudio {
public:
    AlsaAudio();
    ~AlsaAudio();
    int getCards();
    AlsaDeviceInfo getDeviceInfo( int device );
    bool alsaOpen( QString device, AFormat format, unsigned int rate,
                   unsigned int channels, unsigned int buffer_time,
                   unsigned int period_time );
    void alsaWrite( const QByteArray* inputData );
    void alsaClose( void );
    void setVolume ( float vol );
    int bufferSize( void );
    void clearBuffer( void );
private:
    QList<AlsaDeviceInfo> _devices;
    int maxBufferSize;
    // The following static variables are configured in either
    // alsaOpen or alsaSetup and used later in the audio thread
    static int hw_period_size_in;
    static snd_output_t *logs;
    static bool going;
    static snd_pcm_t *alsa_pcm;
    static snd_format* inputf;
    static snd_format* outputf;
    static float volume;
    static QByteArray audioData;
    static convert_func_t alsa_convert_func;
    static convert_channel_func_t alsa_stereo_convert_func;
    static convert_freq_func_t alsa_frequency_convert_func;
    static xmms_convert_buffers *convertb;
    static pthread_t audio_thread;
    static QMutex mutex;
    static bool use_mmap;
    void getDevicesForCard( int card );
    bool alsaSetup( QString device, snd_pcm_uframes_t periodSize, uint periodCount, snd_format *f );
    static void* alsa_loop( void* );
    void run( void );
    void pumpThreadData( void );
    void convertData( void* data, int length );
    void adjustVolume( void* data, int length, AFormat fmt );
    void writeToCard( char *data, int length );
    snd_pcm_sframes_t getAvailableFrames( void );
    int alsa_handle_error( int err );
    int xrun_recover( void );
    int suspend_recover( void );
    int format_from_alsa( snd_pcm_format_t fmt );
    snd_format* snd_format_from_xmms( AFormat fmt, int rate, int channels );
    void alsa_close_pcm( void );
};
#endif
(-) last.fm-1.1.3.orig/src/alsaplayback/alsaplayback.cpp (+205 lines)
Line 0    Link Here 
/***************************************************************************
 *   Copyright (C) 2005 - 2006 by                                          *
 *      Christian Muehlhaeuser, Last.fm Ltd <chris@last.fm>                *
 *      Erik Jaelevik, Last.fm Ltd <erik@last.fm>                          *
 *                                                                         *
 *   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  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA  02111-1307, USA.          *
 ***************************************************************************/
#include <QtGui>
#include "alsaplayback.h"
#include "containerutils.h"
#include "Loqqer.h"
float AlsaPlayback::m_volume = 0.5;
AlsaPlayback::AlsaPlayback() :
    m_audio( 0 ),
    m_timer( new QTimer( this ) )
{
    gLogger.Init( savePath( "playback.log" ), false );
    gLogger.SetLevel( 4 );
    LOGL( 3, "Initialising AlsaAudio Playback" );
    initAudio();
    connect( m_timer, SIGNAL( timeout() ), this, SLOT( checkBuffer() ) );
}
AlsaPlayback::~AlsaPlayback()
{
    delete m_audio;
}
void
AlsaPlayback::setVolume( int volume )
{
    if ( m_audio )
        m_audio->setVolume( (float)volume / 100.0 );
}
QStringList
AlsaPlayback::soundSystems()
{
    QStringList l;
    l << "Alsa";
    return l;
}
QStringList
AlsaPlayback::devices()
{
#ifndef QT_NO_DEBUG
    qDebug() << "Querying audio devices";
#endif
    QStringList l;
    if ( !m_audio )
        return l;
    int cards = m_audio->getCards();
#ifndef QT_NO_DEBUG
    qDebug() << "Device nums:" << cards;
#endif
    for ( int i = 0; i < cards; i++ )
    {
        AlsaDeviceInfo info;
        info = m_audio->getDeviceInfo( i );
#ifndef QT_NO_DEBUG
        qDebug() << "Device name:" << info.name ;
#endif
        l << info.name;
    }
    return l;
}
void
AlsaPlayback::startPlayback()
{
    int channels = 2;
    int sampleRate = 44100;
    int periodSize = 1024;  // According to mplayer, these two are good defaults.
    int periodCount = 16;   // They create a buffer size of 16384 frames.
    QString cardDevice;
    if ( !m_audio )
    {
        LOGL( 1, "No AlsaAudio instance available." );
        goto _error;
    }
    cardDevice = internalSoundCardID();
    if ( !m_audio->alsaOpen( cardDevice, FMT_S16_LE, sampleRate, channels, periodSize, periodCount ) )
        goto _error;
    if ( !m_timer->isActive() )
        m_timer->start( 15 );
    return;
_error:
    // We need to send a stop signal to m_iInput here, otherwise
    // it will keep running and filling up the buffers even though
    // there is no available device.
    emit stop();
    QMessageBox::critical( qApp->activeWindow(), tr("Audio Error"), tr("No soundcard available."));
    return;
}
void
AlsaPlayback::stopPlayback()
{
    if ( !m_audio )
    {
        return;
    }
    m_timer->stop();
    m_audio->alsaClose();
}
void
AlsaPlayback::initAudio()
{
    m_audio = new AlsaAudio();
    if ( m_audio )
    {
        LOGL( 1, "AlsaAudio successfully initialised." );
    }
    else
    {
        LOGL( 1, "Initialising AlsaAudio failed." );
    }
}
void
AlsaPlayback::dataAvailable( const QByteArray &buffer )
{
    m_audio->alsaWrite( &buffer );
}
void
AlsaPlayback::clearBuffers()
{
    m_audio->clearBuffer();
}
void
AlsaPlayback::checkBuffer()
{
    // NOTE: this is wavedata! 16kb of wavedata aprox. equals 1.6kb of mp3
    if ( m_audio->bufferSize() < 16384 * 12 )
        emit needData();
}
QString
AlsaPlayback::internalSoundCardID()
{
    if ( !m_audio )
        return "";
    int cards = m_audio->getCards();
    int cardnum = settingsService()->soundCard();
    if ( cardnum < cards )
    {
        AlsaDeviceInfo info;
        info = m_audio->getDeviceInfo( cardnum );
        return info.device;
    }
    else
        return "default";
}
Q_EXPORT_PLUGIN2( playback, AlsaPlayback )
(-) last.fm-1.1.3.orig/src/alsaplayback/alsaplayback.h (+71 lines)
Line 0    Link Here 
/***************************************************************************
 *   Copyright (C) 2005 - 2006 by                                          *
 *      Christian Muehlhaeuser, Last.fm Ltd <chris@last.fm>                *
 *      Erik Jaelevik, Last.fm Ltd <erik@last.fm>                          *
 *                                                                         *
 *   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  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA  02111-1307, USA.          *
 ***************************************************************************/
#ifndef ALSAPLAYBACK_H
#define ALSAPLAYBACK_H
#include <playbackinterface.h>
#include "alsaaudio.h"
#include <QObject>
#include <QTimer>
class AlsaPlayback : public PlaybackInterface
{
    Q_OBJECT
    Q_INTERFACES( PlaybackInterface )
    public:
        AlsaPlayback();
        ~AlsaPlayback();
        void initAudio();
        float volume() { return m_volume; }
        QStringList soundSystems();
        QStringList devices();
        static float m_volume;
    signals:
        void needData();
        void stop();    // connected to m_iInput::stop()
    public slots:
        void dataAvailable( const QByteArray &buffer );
        void clearBuffers();
        void startPlayback();
        void stopPlayback();
        void setVolume( int volume );
    private:
        AlsaAudio *m_audio;
        QTimer *m_timer;
        QString internalSoundCardID();
    private slots:
        void checkBuffer();
};
#endif
(-) last.fm-1.1.3.orig/src/alsaplayback/alsaplayback.pro (+19 lines)
Line 0    Link Here 
TEMPLATE      = lib
CONFIG       += plugin
INCLUDEPATH  += ../ ../libLastFMTools
LOGGERDIR     = ../build
LIBS         += -L../../bin -lLastFMTools -lasound
OBJECTS_DIR = ../build
MOC_DIR = ../build
UI_DIR = ../build
TARGET  = playback_alsaaudio
DESTDIR = ../../bin/services
include(../../definitions.pro.inc)
HEADERS       = alsaplayback.h alsaaudio.h xconvert.h
SOURCES       = alsaplayback.cpp alsaaudio.cpp xconvert.c
LIBS += $$LOGGERDIR/Loqqer.o
QT += gui xml network
(-) last.fm-1.1.3.orig/src/alsaplayback/precompiled.h (+30 lines)
Line 0    Link Here 
/***************************************************************************
 *   Copyright (C) 2005 - 2006 by                                          *
 *      Christian Muehlhaeuser, Last.fm Ltd <chris@last.fm>                *
 *      Erik Jaelevik, Last.fm Ltd <erik@last.fm>                          *
 *                                                                         *
 *   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  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA  02111-1307, USA.          *
 ***************************************************************************/
/* Add C includes here */
#if defined __cplusplus
    /* Add C++ includes here */
    #include <QtCore>
    #include <QtGui>
    #include <QtNetwork>
    #include <QtXml>
#endif // __cplusplus
(-) last.fm-1.1.3.orig/src/alsaplayback/xconvert.c (+771 lines)
Line 0    Link Here 
/*
 *  Copyright (C) 2001-2003  Haavard Kvaalen <havardk@xmms.org>
 *
 *  Licensed under GNU LGPL version 2.
 */
#include <stdlib.h>
#include <stdint.h>
#include "xconvert.h"
// These are adapted from defines in gtypes.h and glibconfig.h
#ifndef FALSE
#define FALSE    ( 0 )
#endif
#ifndef TRUE
#define TRUE    ( !FALSE )
#endif
# define GUINT16_SWAP_LE_BE( val )                          \
    ( ( uint16_t )                                          \
        (                                                   \
            ( uint16_t ) ( ( uint16_t ) ( val ) >> 8 ) |    \
            ( uint16_t ) ( ( uint16_t ) ( val ) << 8 )      \
        )                                                   \
    )
# define GINT16_SWAP_LE_BE( val )    ( ( int16_t ) GUINT16_SWAP_LE_BE ( val ) )
#ifdef WORDS_BIGENDIAN
# define IS_BIG_ENDIAN TRUE
# define GINT16_TO_BE( val )       ( ( int16_t ) ( val ) )
# define GINT16_FROM_BE( val )     ( ( int16_t ) ( val ) )
# define GUINT16_TO_BE( val )      ( ( uint16_t ) ( val ) )
# define GUINT16_FROM_BE( val )    ( ( uint16_t ) ( val ) )
# define GUINT16_TO_LE( val )      ( GUINT16_SWAP_LE_BE ( val ) )
# define GUINT16_FROM_LE( val )    ( GUINT16_SWAP_LE_BE ( val ) )
# define GINT16_TO_LE( val )       ( ( int16_t ) GUINT16_SWAP_LE_BE ( val ) )
# define GINT16_FROM_LE( val )     ( ( int16_t ) GUINT16_SWAP_LE_BE ( val ) )
#else
# define IS_BIG_ENDIAN FALSE
# define GINT16_TO_LE( val )       ( ( int16_t ) ( val ) )
# define GINT16_FROM_LE( val )     ( ( int16_t ) ( val ) )
# define GUINT16_TO_LE( val )      ( ( uint16_t ) ( val ) )
# define GUINT16_FROM_LE( val )    ( ( uint16_t ) ( val ) )
# define GUINT16_TO_BE( val )      ( GUINT16_SWAP_LE_BE ( val ) )
# define GUINT16_FROM_BE( val )    ( GUINT16_SWAP_LE_BE ( val ) )
# define GINT16_TO_BE( val )       ( ( int16_t ) GUINT16_SWAP_LE_BE ( val ) )
# define GINT16_FROM_BE( val )     ( ( int16_t ) GUINT16_SWAP_LE_BE ( val ) )
#endif
struct buffer {
    void *buffer;
    uint size;
};
struct xmms_convert_buffers {
    struct buffer format_buffer, stereo_buffer, freq_buffer;
};
struct xmms_convert_buffers* xmms_convert_buffers_new( void )
{
    return calloc( 1, sizeof( struct xmms_convert_buffers ) );
}
static void* convert_get_buffer( struct buffer *buffer, size_t size )
{
    if ( size > 0 && size <= buffer->size )
        return buffer->buffer;
    buffer->size = size;
    buffer->buffer = realloc( buffer->buffer, size );
    return buffer->buffer;
}
void xmms_convert_buffers_free( struct xmms_convert_buffers* buf )
{
    convert_get_buffer( &buf->format_buffer, 0 );
    convert_get_buffer( &buf->stereo_buffer, 0 );
    convert_get_buffer( &buf->freq_buffer, 0 );
}
void xmms_convert_buffers_destroy( struct xmms_convert_buffers* buf )
{
    if ( !buf )
        return;
    xmms_convert_buffers_free( buf );
    free( buf );
}
static int convert_swap_endian( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint16_t *ptr = *data;
    int i;
    for ( i = 0; i < length; i += 2, ptr++ )
        *ptr = GUINT16_SWAP_LE_BE( *ptr );
    return i;
}
static int convert_swap_sign_and_endian_to_native( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint16_t *ptr = *data;
    int i;
    for ( i = 0; i < length; i += 2, ptr++ )
        *ptr = GUINT16_SWAP_LE_BE( *ptr ) ^ 1 << 15;
    return i;
}
static int convert_swap_sign_and_endian_to_alien( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint16_t *ptr = *data;
    int i;
    for ( i = 0; i < length; i += 2, ptr++ )
        *ptr = GUINT16_SWAP_LE_BE( *ptr ^ 1 << 15 );
    return i;
}
static int convert_swap_sign16( struct xmms_convert_buffers* buf, void **data, int length )
{
    int16_t *ptr = *data;
    int i;
    for ( i = 0; i < length; i += 2, ptr++ )
        *ptr ^= 1 << 15;
    return i;
}
static int convert_swap_sign8( struct xmms_convert_buffers* buf, void **data, int length )
{
    int8_t *ptr = *data;
    int i;
    for ( i = 0; i < length; i++ )
        *ptr++ ^= 1 << 7;
    return i;
}
static int convert_to_8_native_endian( struct xmms_convert_buffers* buf, void **data, int length )
{
    int8_t *output = *data;
    int16_t *input = *data;
    int i;
    for ( i = 0; i < length / 2; i++ )
        *output++ = *input++ >> 8;
    return i;
}
static int convert_to_8_native_endian_swap_sign( struct xmms_convert_buffers* buf, void **data, int length )
{
    int8_t *output = *data;
    int16_t *input = *data;
    int i;
    for ( i = 0; i < length / 2; i++ )
        *output++ = ( *input++ >> 8 ) ^ ( 1 << 7 );
    return i;
}
static int convert_to_8_alien_endian( struct xmms_convert_buffers* buf, void **data, int length )
{
    int8_t *output = *data;
    int16_t *input = *data;
    int i;
    for ( i = 0; i < length / 2; i++ )
        *output++ = *input++ & 0xff;
    return i;
}
static int convert_to_8_alien_endian_swap_sign( struct xmms_convert_buffers* buf, void **data, int length )
{
    int8_t *output = *data;
    int16_t *input = *data;
    int i;
    for ( i = 0; i < length / 2; i++ )
        *output++ = ( *input++ & 0xff ) ^ ( 1 << 7 );
    return i;
}
static int convert_to_16_native_endian( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint8_t *input = *data;
    uint16_t *output;
    int i;
    *data = convert_get_buffer( &buf->format_buffer, length * 2 );
    output = *data;
    for ( i = 0; i < length; i++ )
        *output++ = *input++ << 8;
    return i * 2;
}
static int convert_to_16_native_endian_swap_sign( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint8_t *input = *data;
    uint16_t *output;
    int i;
    *data = convert_get_buffer( &buf->format_buffer, length * 2 );
    output = *data;
    for ( i = 0; i < length; i++ )
        *output++ = ( *input++ << 8 ) ^ ( 1 << 15 );
    return i * 2;
}
static int convert_to_16_alien_endian( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint8_t *input = *data;
    uint16_t *output;
    int i;
    *data = convert_get_buffer( &buf->format_buffer, length * 2 );
    output = *data;
    for ( i = 0; i < length; i++ )
        *output++ = *input++;
    return i * 2;
}
static int convert_to_16_alien_endian_swap_sign( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint8_t *input = *data;
    uint16_t *output;
    int i;
    *data = convert_get_buffer( &buf->format_buffer, length * 2 );
    output = *data;
    for ( i = 0; i < length; i++ )
        *output++ = *input++ ^ ( 1 << 7 );
    return i * 2;
}
static AFormat unnativize( AFormat fmt )
{
    if ( fmt == FMT_S16_NE )
    {
        if ( IS_BIG_ENDIAN )
            return FMT_S16_BE;
        else
            return FMT_S16_LE;
    }
    if ( fmt == FMT_U16_NE )
    {
        if ( IS_BIG_ENDIAN )
            return FMT_U16_BE;
        else
            return FMT_U16_LE;
    }
    return fmt;
}
convert_func_t xmms_convert_get_func( AFormat output, AFormat input )
{
    output = unnativize( output );
    input = unnativize( input );
    if ( output == input )
        return NULL;
    if ( ( output == FMT_U16_BE && input == FMT_U16_LE ) ||
         ( output == FMT_U16_LE && input == FMT_U16_BE ) ||
         ( output == FMT_S16_BE && input == FMT_S16_LE ) ||
         ( output == FMT_S16_LE && input == FMT_S16_BE ) )
        return convert_swap_endian;
    if ( ( output == FMT_U16_BE && input == FMT_S16_BE ) ||
         ( output == FMT_U16_LE && input == FMT_S16_LE ) ||
         ( output == FMT_S16_BE && input == FMT_U16_BE ) ||
         ( output == FMT_S16_LE && input == FMT_U16_LE ) )
        return convert_swap_sign16;
    if ( ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_BE && input == FMT_S16_LE ) ||
           ( output == FMT_S16_BE && input == FMT_U16_LE ) ) ) ||
        ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_LE && input == FMT_S16_BE ) ||
           ( output == FMT_S16_LE && input == FMT_U16_BE ) ) ) )
        return convert_swap_sign_and_endian_to_native;
    if ( ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_BE && input == FMT_S16_LE ) ||
           ( output == FMT_S16_BE && input == FMT_U16_LE ) ) ) ||
        ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_LE && input == FMT_S16_BE ) ||
           ( output == FMT_S16_LE && input == FMT_U16_BE ) ) ) )
        return convert_swap_sign_and_endian_to_alien;
    if ( ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_U16_BE ) ||
           ( output == FMT_S8 && input == FMT_S16_BE ) ) ) ||
        ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_U16_LE ) ||
           ( output == FMT_S8 && input == FMT_S16_LE ) ) ) )
        return convert_to_8_native_endian;
    if ( ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_S16_BE ) ||
           ( output == FMT_S8 && input == FMT_U16_BE ) ) ) ||
        ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_S16_LE ) ||
           ( output == FMT_S8 && input == FMT_U16_LE ) ) ) )
        return convert_to_8_native_endian_swap_sign;
    if ( ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_U16_BE ) ||
           ( output == FMT_S8 && input == FMT_S16_BE ) ) ) ||
        ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_U16_LE ) ||
           ( output == FMT_S8 && input == FMT_S16_LE ) ) ) )
        return convert_to_8_alien_endian;
    if ( ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_S16_BE ) ||
           ( output == FMT_S8 && input == FMT_U16_BE ) ) ) ||
        ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U8 && input == FMT_S16_LE ) ||
           ( output == FMT_S8 && input == FMT_U16_LE ) ) ) )
        return convert_to_8_alien_endian_swap_sign;
    if ( ( output == FMT_U8 && input == FMT_S8 ) ||
         ( output == FMT_S8 && input == FMT_U8 ) )
        return convert_swap_sign8;
    if ( ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_BE && input == FMT_U8 ) ||
           ( output == FMT_S16_BE && input == FMT_S8 ) ) ) ||
        ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_LE && input == FMT_U8 ) ||
           ( output == FMT_S16_LE && input == FMT_S8 ) ) ) )
        return convert_to_16_native_endian;
    if ( ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_BE && input == FMT_S8 ) ||
           ( output == FMT_S16_BE && input == FMT_U8 ) ) ) ||
        ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_LE && input == FMT_S8 ) ||
           ( output == FMT_S16_LE && input == FMT_U8 ) ) ) )
        return convert_to_16_native_endian_swap_sign;
    if ( ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_BE && input == FMT_U8 ) ||
           ( output == FMT_S16_BE && input == FMT_S8 ) ) ) ||
        ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_LE && input == FMT_U8 ) ||
           ( output == FMT_S16_LE && input == FMT_S8 ) ) ) )
        return convert_to_16_alien_endian;
    if ( ( !IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_BE && input == FMT_S8 ) ||
           ( output == FMT_S16_BE && input == FMT_U8 ) ) ) ||
        ( IS_BIG_ENDIAN &&
         ( ( output == FMT_U16_LE && input == FMT_S8 ) ||
           ( output == FMT_S16_LE && input == FMT_U8 ) ) ) )
        return convert_to_16_alien_endian_swap_sign;
    //g_warning( "Translation needed, but not available.\n"
    //      "Input: %d; Output %d.", input, output );
    return NULL;
}
static int convert_mono_to_stereo( struct xmms_convert_buffers* buf, void **data, int length, int b16 )
{
    int i;
    void *outbuf = convert_get_buffer( &buf->stereo_buffer, length * 2 );
    if ( b16 )
    {
        uint16_t *output = outbuf, *input = *data;
        for ( i = 0; i < length / 2; i++ )
        {
            *output++ = *input;
            *output++ = *input;
            input++;
        }
    }
    else
    {
        uint8_t *output = outbuf, *input = *data;
        for ( i = 0; i < length; i++ )
        {
            *output++ = *input;
            *output++ = *input;
            input++;
        }
    }
    *data = outbuf;
    return length * 2;
}
static int convert_mono_to_stereo_8( struct xmms_convert_buffers* buf, void **data, int length )
{
    return convert_mono_to_stereo( buf, data, length, FALSE );
}
static int convert_mono_to_stereo_16( struct xmms_convert_buffers* buf, void **data, int length )
{
    return convert_mono_to_stereo( buf, data, length, TRUE );
}
static int convert_stereo_to_mono_u8( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint8_t *output = *data, *input = *data;
    int i;
    for ( i = 0; i < length / 2; i++ )
    {
        uint16_t tmp;
        tmp = *input++;
        tmp += *input++;
        *output++ = tmp / 2;
    }
    return length / 2;
}
static int convert_stereo_to_mono_s8( struct xmms_convert_buffers* buf, void **data, int length )
{
    int8_t *output = *data, *input = *data;
    int i;
    for ( i = 0; i < length / 2; i++ )
    {
        int16_t tmp;
        tmp = *input++;
        tmp += *input++;
        *output++ = tmp / 2;
    }
    return length / 2;
}
static int convert_stereo_to_mono_u16le( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint16_t *output = *data, *input = *data;
    int i;
    for ( i = 0; i < length / 4; i++ )
    {
        uint32_t tmp;
        uint16_t stmp;
        tmp = GUINT16_FROM_LE( *input );
        input++;
        tmp += GUINT16_FROM_LE( *input );
        input++;
        stmp = tmp / 2;
        *output++ = GUINT16_TO_LE( stmp );
    }
    return length / 2;
}
static int convert_stereo_to_mono_u16be( struct xmms_convert_buffers* buf, void **data, int length )
{
    uint16_t *output = *data, *input = *data;
    int i;
    for ( i = 0; i < length / 4; i++ )
    {
        uint32_t tmp;
        uint16_t stmp;
        tmp = GUINT16_FROM_BE( *input );
        input++;
        tmp += GUINT16_FROM_BE( *input );
        input++;
        stmp = tmp / 2;
        *output++ = GUINT16_TO_BE( stmp );
    }
    return length / 2;
}
static int convert_stereo_to_mono_s16le( struct xmms_convert_buffers* buf, void **data, int length )
{
    int16_t *output = *data, *input = *data;
    int i;
    for ( i = 0; i < length / 4; i++ )
    {
        int32_t tmp;
        int16_t stmp;
        tmp = GINT16_FROM_LE( *input );
        input++;
        tmp += GINT16_FROM_LE( *input );
        input++;
        stmp = tmp / 2;
        *output++ = GINT16_TO_LE( stmp );
    }
    return length / 2;
}
static int convert_stereo_to_mono_s16be( struct xmms_convert_buffers* buf, void **data, int length )
{
    int16_t *output = *data, *input = *data;
    int i;
    for ( i = 0; i < length / 4; i++ )
    {
        int32_t tmp;
        int16_t stmp;
        tmp = GINT16_FROM_BE( *input );
        input++;
        tmp += GINT16_FROM_BE( *input );
        input++;
        stmp = tmp / 2;
        *output++ = GINT16_TO_BE( stmp );
    }
    return length / 2;
}
convert_channel_func_t xmms_convert_get_channel_func( AFormat fmt, int output, int input )
{
    fmt = unnativize( fmt );
    if ( output == input )
        return NULL;
    if ( input == 1 && output == 2 )
        switch ( fmt )
        {
            case FMT_U8:
            case FMT_S8:
                return convert_mono_to_stereo_8;
            case FMT_U16_LE:
            case FMT_U16_BE:
            case FMT_S16_LE:
            case FMT_S16_BE:
                return convert_mono_to_stereo_16;
            default:
                //g_warning( "Unknown format: %d"
                //      "No conversion available.", fmt );
                return NULL;
        }
    if ( input == 2 && output == 1 )
        switch ( fmt )
        {
            case FMT_U8:
                return convert_stereo_to_mono_u8;
            case FMT_S8:
                return convert_stereo_to_mono_s8;
            case FMT_U16_LE:
                return convert_stereo_to_mono_u16le;
            case FMT_U16_BE:
                return convert_stereo_to_mono_u16be;
            case FMT_S16_LE:
                return convert_stereo_to_mono_s16le;
            case FMT_S16_BE:
                return convert_stereo_to_mono_s16be;
            default:
                //g_warning( "Unknown format: %d.  "
                //      "No conversion available.", fmt );
                return NULL;
        }
    //g_warning( "Input has %d channels, soundcard uses %d channels\n"
    //      "No conversion is available", input, output );
    return NULL;
}
#define RESAMPLE_STEREO( sample_type, bswap )                   \
do {                                                            \
    const int shift = sizeof ( sample_type );                   \
        int i, in_samples, out_samples, x, delta;               \
    sample_type *inptr = *data, *outptr;                        \
    uint nlen = ( ( ( length >> shift ) * ofreq ) / ifreq );    \
    void *nbuf;                                                 \
    if ( nlen == 0 )                                            \
        break;                                                  \
    nlen <<= shift;                                             \
    if ( bswap )                                                \
        convert_swap_endian( NULL, data, length );              \
    nbuf = convert_get_buffer( &buf->freq_buffer, nlen );       \
    outptr = nbuf;                                              \
    in_samples = length >> shift;                               \
        out_samples = nlen >> shift;                            \
    delta = ( in_samples << 12 ) / out_samples;                 \
    for ( x = 0, i = 0; i < out_samples; i++ )                  \
    {                                                           \
        int x1, frac;                                           \
        x1 = ( x >> 12 ) << 12;                                 \
        frac = x - x1;                                          \
        *outptr++ =                                             \
            ( ( inptr[( x1 >> 12 ) << 1] *                      \
              ( ( 1<<12 ) - frac ) +                            \
              inptr[( ( x1 >> 12 ) + 1 ) << 1] *                \
              frac ) >> 12 );                                   \
        *outptr++ =                                             \
            ( ( inptr[( ( x1 >> 12 ) << 1 ) + 1] *              \
              ( ( 1<<12 ) - frac ) +                            \
              inptr[( ( ( x1 >> 12 ) + 1 ) << 1 ) + 1] *        \
              frac ) >> 12 );                                   \
        x += delta;                                             \
    }                                                           \
    if ( bswap )                                                \
        convert_swap_endian( NULL, &nbuf, nlen );               \
    *data = nbuf;                                               \
    return nlen;                                                \
} while ( 0 )
#define RESAMPLE_MONO( sample_type, bswap )                     \
do {                                                            \
    const int shift = sizeof ( sample_type ) - 1;               \
        int i, x, delta, in_samples, out_samples;               \
    sample_type *inptr = *data, *outptr;                        \
    uint nlen = ( ( ( length >> shift ) * ofreq ) / ifreq );    \
    void *nbuf;                                                 \
    if ( nlen == 0 )                                            \
        break;                                                  \
    nlen <<= shift;                                             \
    if ( bswap )                                                \
        convert_swap_endian( NULL, data, length );              \
    nbuf = convert_get_buffer( &buf->freq_buffer, nlen );       \
    outptr = nbuf;                                              \
    in_samples = length >> shift;                               \
        out_samples = nlen >> shift;                            \
    delta = ( ( length >> shift ) << 12 ) / out_samples;        \
    for ( x = 0, i = 0; i < out_samples; i++ )                  \
    {                                                           \
        int x1, frac;                                           \
        x1 = ( x >> 12 ) << 12;                                 \
        frac = x - x1;                                          \
        *outptr++ =                                             \
            ( ( inptr[x1 >> 12] * ( ( 1<<12 ) - frac ) +        \
              inptr[( x1 >> 12 ) + 1] * frac ) >> 12 );         \
        x += delta;                                             \
    }                                                           \
    if ( bswap )                                                \
        convert_swap_endian( NULL, &nbuf, nlen );               \
    *data = nbuf;                                               \
    return nlen;                                                \
} while ( 0 )
static int convert_resample_stereo_s16ne( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_STEREO( int16_t, FALSE );
    return 0;
}
static int convert_resample_stereo_s16ae( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_STEREO( int16_t, TRUE );
    return 0;
}
static int convert_resample_stereo_u16ne( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_STEREO( uint16_t, FALSE );
    return 0;
}
static int convert_resample_stereo_u16ae( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_STEREO( uint16_t, TRUE );
    return 0;
}
static int convert_resample_mono_s16ne( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_MONO( int16_t, FALSE );
    return 0;
}
static int convert_resample_mono_s16ae( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_MONO( int16_t, TRUE );
    return 0;
}
static int convert_resample_mono_u16ne( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_MONO( uint16_t, FALSE );
    return 0;
}
static int convert_resample_mono_u16ae( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_MONO( uint16_t, TRUE );
    return 0;
}
static int convert_resample_stereo_u8( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_STEREO( uint8_t, FALSE );
    return 0;
}
static int convert_resample_mono_u8( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_MONO( uint8_t, FALSE );
    return 0;
}
static int convert_resample_stereo_s8( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_STEREO( int8_t, FALSE );
    return 0;
}
static int convert_resample_mono_s8( struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq )
{
    RESAMPLE_MONO( int8_t, FALSE );
    return 0;
}
convert_freq_func_t xmms_convert_get_frequency_func( AFormat fmt, int channels )
{
    fmt = unnativize( fmt );
    //g_message( "fmt %d, channels: %d", fmt, channels );
    if ( channels < 1 || channels > 2 )
    {
        //g_warning( "Unsupported number of channels: %d.  "
        //      "Resample function not available", channels );
        return NULL;
    }
    if ( (  IS_BIG_ENDIAN && fmt == FMT_U16_BE ) ||
         ( !IS_BIG_ENDIAN && fmt == FMT_U16_LE ) )
    {
        if ( channels == 1 )
            return convert_resample_mono_u16ne;
        else
            return convert_resample_stereo_u16ne;
    }
    if ( (  IS_BIG_ENDIAN && fmt == FMT_S16_BE ) ||
         ( !IS_BIG_ENDIAN && fmt == FMT_S16_LE ) )
    {
        if ( channels == 1 )
            return convert_resample_mono_s16ne;
        else
            return convert_resample_stereo_s16ne;
    }
    if ( ( !IS_BIG_ENDIAN && fmt == FMT_U16_BE ) ||
         (  IS_BIG_ENDIAN && fmt == FMT_U16_LE ) )
    {
        if ( channels == 1 )
            return convert_resample_mono_u16ae;
        else
            return convert_resample_stereo_u16ae;
    }
    if ( ( !IS_BIG_ENDIAN && fmt == FMT_S16_BE ) ||
         (  IS_BIG_ENDIAN && fmt == FMT_S16_LE ) )
    {
        if ( channels == 1 )
            return convert_resample_mono_s16ae;
        else
            return convert_resample_stereo_s16ae;
    }
    if ( fmt == FMT_U8 )
    {
        if ( channels == 1 )
            return convert_resample_mono_u8;
        else
            return convert_resample_stereo_u8;
    }
    if ( fmt == FMT_S8 )
    {
        if ( channels == 1 )
            return convert_resample_mono_s8;
        else
            return convert_resample_stereo_s8;
    }
    //g_warning( "Resample function not available"
    //      "Format %d.", fmt );
    return NULL;
}
(-) last.fm-1.1.3.orig/src/alsaplayback/xconvert.h (+39 lines)
Line 0    Link Here 
/*
 *  Copyright (C) 2003  Haavard Kvaalen <havardk@xmms.org>
 *
 *  Licensed under GNU LGPL version 2.
 */
#ifdef __cplusplus
extern "C" {
#endif
typedef enum
{
    FMT_U8, FMT_S8, FMT_U16_LE, FMT_U16_BE, FMT_U16_NE, FMT_S16_LE, FMT_S16_BE, FMT_S16_NE
}
AFormat;
struct xmms_convert_buffers;
struct xmms_convert_buffers* xmms_convert_buffers_new(void);
/*
 * Free the data assosiated with the buffers, without destroying the
 * context.  The context can be reused.
 */
void xmms_convert_buffers_free(struct xmms_convert_buffers* buf);
void xmms_convert_buffers_destroy(struct xmms_convert_buffers* buf);
typedef int (*convert_func_t)(struct xmms_convert_buffers* buf, void **data, int length);
typedef int (*convert_channel_func_t)(struct xmms_convert_buffers* buf, void **data, int length);
typedef int (*convert_freq_func_t)(struct xmms_convert_buffers* buf, void **data, int length, int ifreq, int ofreq);
convert_func_t xmms_convert_get_func(AFormat output, AFormat input);
convert_channel_func_t xmms_convert_get_channel_func(AFormat fmt, int output, int input);
convert_freq_func_t xmms_convert_get_frequency_func(AFormat fmt, int channels);
#ifdef __cplusplus
}
#endif
(-) last.fm-1.1.3.orig/src/container.cpp (+1 lines)
 Lines 517-522    Link Here 
    connect( m_iInput,      SIGNAL( newData( QByteArray ) ),  m_iTranscode, SLOT( dataAvailable( QByteArray ) ) );
    connect( m_iInput,      SIGNAL( newData( QByteArray ) ),  m_iTranscode, SLOT( dataAvailable( QByteArray ) ) );
    connect( m_iTranscode,  SIGNAL( newData( QByteArray ) ),  m_iPlayback,  SLOT( dataAvailable( QByteArray ) ) );
    connect( m_iTranscode,  SIGNAL( newData( QByteArray ) ),  m_iPlayback,  SLOT( dataAvailable( QByteArray ) ) );
    connect( m_iPlayback,   SIGNAL( needData() ),             m_iInput,     SLOT( requestData() ) );
    connect( m_iPlayback,   SIGNAL( needData() ),             m_iInput,     SLOT( requestData() ) );
    connect( m_iPlayback,   SIGNAL( stop() ),                 m_iInput,     SLOT( stopStreaming() ) );
    // Auto updater
    // Auto updater
    connect( &m_updater, SIGNAL( updateCheckDone( bool, bool, QString ) ),
    connect( &m_updater, SIGNAL( updateCheckDone( bool, bool, QString ) ),
(-) last.fm-1.1.3.orig/src/libLastFMTools/containerutils.cpp (-1 / +1 lines)
 Lines 288-294    Link Here 
{
{
    if ( g_playbackService == NULL )
    if ( g_playbackService == NULL )
    {
    {
        QObject* obj = loadService( "playback_rtaudio" );
        QObject* obj = loadService( "playback_alsaaudio" );
        g_playbackService = qobject_cast<PlaybackInterface*>( obj );
        g_playbackService = qobject_cast<PlaybackInterface*>( obj );
    }
    }