mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-31 09:32:03 +02:00
* Getting rid of old & obsolete audioengine / transcoder stuff.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
SET( OS_SPECIFIC_LINK_LIBRARIES
|
||||
${OS_SPECIFIC_LINK_LIBRARIES}
|
||||
alsaplayback
|
||||
tomahawklib
|
||||
)
|
||||
|
||||
|
@@ -10,8 +10,6 @@ SET( OS_SPECIFIC_LINK_LIBRARIES
|
||||
/System/Library/Frameworks/DiskArbitration.framework
|
||||
/System/Library/Frameworks/Foundation.framework
|
||||
/System/Library/Frameworks/IOKit.framework
|
||||
|
||||
rtaudio
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
|
@@ -135,13 +135,10 @@ INCLUDE_DIRECTORIES(
|
||||
libtomahawk/utils
|
||||
mac
|
||||
|
||||
${THIRDPARTY_DIR}/alsa-playback
|
||||
${THIRDPARTY_DIR}/rtaudio
|
||||
${THIRDPARTY_DIR}/qxt/qxtweb-standalone/qxtweb/
|
||||
${THIRDPARTY_DIR}/qtweetlib/qtweetlib/src
|
||||
${THIRDPARTY_DIR}/qtweetlib/tomahawk-custom
|
||||
|
||||
|
||||
${TAGLIB_INCLUDES}
|
||||
${LIBECHONEST_INCLUDE_DIR}
|
||||
)
|
||||
|
@@ -13,7 +13,6 @@ SET( OS_SPECIFIC_LINK_LIBRARIES
|
||||
${OS_SPECIFIC_LINK_LIBRARIES}
|
||||
|
||||
# third party shipped with tomahawk
|
||||
${CMAKE_BINARY_DIR}/thirdparty/rtaudio/librtaudio.dll
|
||||
|
||||
# system libs
|
||||
"secur32.dll"
|
||||
|
@@ -28,10 +28,6 @@ set( libSources
|
||||
|
||||
sip/SipPlugin.cpp
|
||||
|
||||
|
||||
audio/madtranscode.cpp
|
||||
audio/vorbistranscode.cpp
|
||||
audio/flactranscode.cpp
|
||||
audio/audioengine.cpp
|
||||
|
||||
database/database.cpp
|
||||
@@ -168,10 +164,6 @@ set( libHeaders
|
||||
|
||||
sip/SipPlugin.h
|
||||
|
||||
audio/transcodeinterface.h
|
||||
audio/madtranscode.h
|
||||
audio/vorbistranscode.h
|
||||
audio/flactranscode.h
|
||||
audio/audioengine.h
|
||||
|
||||
database/database.h
|
||||
@@ -311,8 +303,6 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.
|
||||
|
||||
${THIRDPARTY_DIR}/libportfwd/include
|
||||
${THIRDPARTY_DIR}/qxt/qxtweb-standalone/qxtweb
|
||||
${THIRDPARTY_DIR}/rtaudio
|
||||
${THIRDPARTY_DIR}/alsa-playback
|
||||
${THIRDPARTY_DIR}/jdns
|
||||
${THIRDPARTY_DIR}/jdns/jdns
|
||||
${THIRDPARTY_DIR}/jdns/jdnsshared
|
||||
@@ -321,13 +311,9 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.
|
||||
|
||||
|
||||
IF( WIN32 )
|
||||
SET( libSources ${libSources} audio/rtaudiooutput.cpp )
|
||||
SET( libHeaders ${libHeaders} audio/rtaudiooutput.h )
|
||||
|
||||
SET( OS_SPECIFIC_LINK_LIBRARIES
|
||||
${OS_SPECIFIC_LINK_LIBRARIES}
|
||||
# Thirdparty
|
||||
${CMAKE_BINARY_DIR}/thirdparty/rtaudio/librtaudio.dll
|
||||
# System
|
||||
"iphlpapi.a"
|
||||
"ws2_32.dll"
|
||||
@@ -343,13 +329,9 @@ IF( APPLE )
|
||||
FIND_LIBRARY( COREFOUNDATION_LIBRARY CoreFoundation )
|
||||
MARK_AS_ADVANCED( COREAUDIO_LIBRARY COREFOUNDATION_LIBRARY )
|
||||
|
||||
SET( libSources ${libSources} audio/rtaudiooutput.cpp )
|
||||
SET( libHeaders ${libHeaders} audio/rtaudiooutput.h )
|
||||
|
||||
SET( OS_SPECIFIC_LINK_LIBRARIES
|
||||
${OS_SPECIFIC_LINK_LIBRARIES}
|
||||
# Thirdparty
|
||||
rtaudio
|
||||
# System
|
||||
${COREAUDIO_LIBRARY}
|
||||
${COREFOUNDATION_LIBRARY}
|
||||
@@ -360,7 +342,6 @@ IF( UNIX AND NOT APPLE )
|
||||
SET( OS_SPECIFIC_LINK_LIBRARIES
|
||||
${OS_SPECIFIC_LINK_LIBRARIES}
|
||||
# Thirdparty
|
||||
alsaplayback
|
||||
)
|
||||
ENDIF( UNIX AND NOT APPLE )
|
||||
|
||||
@@ -379,12 +360,6 @@ target_link_libraries( tomahawklib
|
||||
# Thirdparty shipped with tomahawk
|
||||
portfwd
|
||||
|
||||
# soon to be removed by phonon-dependency
|
||||
FLAC++
|
||||
ogg
|
||||
vorbisfile
|
||||
mad
|
||||
|
||||
# External deps
|
||||
${TAGLIB_LIBRARIES}
|
||||
${QJSON_LIBRARIES}
|
||||
|
@@ -9,8 +9,6 @@
|
||||
#include "result.h"
|
||||
#include "typedefs.h"
|
||||
|
||||
#include "transcodeinterface.h"
|
||||
|
||||
#include "dllmacro.h"
|
||||
|
||||
#define AUDIO_VOLUME_STEP 5
|
||||
|
@@ -1,146 +0,0 @@
|
||||
#include "flactranscode.h"
|
||||
|
||||
|
||||
FLACTranscode::FLACTranscode()
|
||||
: m_FLACRunning( false )
|
||||
, m_finished( false )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
init();
|
||||
set_metadata_respond_all();
|
||||
}
|
||||
|
||||
|
||||
FLACTranscode::~FLACTranscode()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FLACTranscode::onSeek( int seconds )
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_buffer.clear();
|
||||
m_outBuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FLACTranscode::clearBuffers()
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_FLACRunning = false;
|
||||
m_finished = false;
|
||||
|
||||
m_buffer.clear();
|
||||
m_outBuffer.clear();
|
||||
|
||||
flush();
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FLACTranscode::processData( const QByteArray& data, bool finish )
|
||||
{
|
||||
m_mutex.lock();
|
||||
m_buffer.append( data );
|
||||
m_mutex.unlock();
|
||||
|
||||
while ( m_buffer.size() >= FLAC_BUFFER )
|
||||
{
|
||||
process_single();
|
||||
}
|
||||
|
||||
m_finished = finish;
|
||||
}
|
||||
|
||||
|
||||
::FLAC__StreamDecoderReadStatus
|
||||
FLACTranscode::read_callback( FLAC__byte buffer[], size_t *bytes )
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
if ( *bytes > (unsigned int)m_buffer.size() )
|
||||
*bytes = m_buffer.size();
|
||||
|
||||
memcpy( buffer, (char*)m_buffer.data(), *bytes );
|
||||
m_buffer.remove( 0, *bytes );
|
||||
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
::FLAC__StreamDecoderWriteStatus
|
||||
FLACTranscode::write_callback( const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[] )
|
||||
{
|
||||
union PCMDATA
|
||||
{
|
||||
FLAC__int32 i;
|
||||
unsigned char b[2];
|
||||
} pcmDataLeft, pcmDataRight;
|
||||
|
||||
for ( unsigned int sample = 0; sample < frame->header.blocksize; sample++ )
|
||||
{
|
||||
pcmDataLeft.i = buffer[0][sample];
|
||||
pcmDataRight.i = buffer[1][sample];
|
||||
|
||||
m_outBuffer.append( pcmDataLeft.b[0] );
|
||||
m_outBuffer.append( pcmDataLeft.b[1] );
|
||||
m_outBuffer.append( pcmDataRight.b[0] );
|
||||
m_outBuffer.append( pcmDataRight.b[1] );
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
::FLAC__StreamDecoderSeekStatus
|
||||
FLACTranscode::seek_callback(FLAC__uint64 absolute_byte_offset)
|
||||
{
|
||||
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FLACTranscode::metadata_callback( const ::FLAC__StreamMetadata *metadata )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << metadata->is_last;
|
||||
|
||||
switch ( metadata->type )
|
||||
{
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
{
|
||||
FLAC::Metadata::StreamInfo stream_info( (::FLAC__StreamMetadata *)metadata, true );
|
||||
|
||||
// Try to determine samplerate
|
||||
qDebug() << "FLACTranscode( BitsPerSample:" << stream_info.get_bits_per_sample() << "Samplerate:" << stream_info.get_sample_rate() << "Channels:" << stream_info.get_channels() << ")";
|
||||
emit streamInitialized( stream_info.get_sample_rate(), stream_info.get_channels() );
|
||||
|
||||
m_FLACRunning = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
qDebug() << "Not handling type:" << metadata->type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
FLACTranscode::error_callback( ::FLAC__StreamDecoderErrorStatus status )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << status;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
FLACTranscode::eof_callback()
|
||||
{
|
||||
return ( m_buffer.isEmpty() && m_finished );
|
||||
}
|
@@ -1,69 +0,0 @@
|
||||
/*! \class FLACTranscode
|
||||
\brief Transcoding plugin for FLAC streams.
|
||||
*/
|
||||
|
||||
#ifndef FLAC_TRANSCODE_H
|
||||
#define FLAC_TRANSCODE_H
|
||||
|
||||
#include "transcodeinterface.h"
|
||||
|
||||
#include <FLAC/format.h>
|
||||
#include <FLAC++/decoder.h>
|
||||
#include <FLAC++/metadata.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
|
||||
#include "dllmacro.h"
|
||||
|
||||
#define FLAC_BUFFER 32768 * 36
|
||||
#define FLAC_BUFFER_PREFERRED 32768
|
||||
|
||||
class DLLEXPORT FLACTranscode : public TranscodeInterface , protected FLAC::Decoder::Stream
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FLACTranscode();
|
||||
~FLACTranscode();
|
||||
|
||||
const QStringList supportedTypes() const { QStringList l; l << "audio/flac" << "flac"; return l; }
|
||||
|
||||
int needData() { return FLAC_BUFFER - m_buffer.count(); }
|
||||
bool haveData() { return !m_outBuffer.isEmpty(); }
|
||||
|
||||
unsigned int preferredDataSize() { return FLAC_BUFFER_PREFERRED; }
|
||||
|
||||
QByteArray data() { QByteArray b = m_outBuffer; m_outBuffer.clear(); return b; }
|
||||
|
||||
QMutex* mutex() { return &m_mutex; }
|
||||
QByteArray* buffer() { return &m_buffer; }
|
||||
|
||||
signals:
|
||||
void streamInitialized( long sampleRate, int channels );
|
||||
|
||||
public slots:
|
||||
void onSeek( int seconds );
|
||||
void clearBuffers();
|
||||
void processData( const QByteArray& data, bool finish );
|
||||
|
||||
protected:
|
||||
virtual ::FLAC__StreamDecoderReadStatus read_callback( FLAC__byte buffer[], size_t *bytes );
|
||||
virtual ::FLAC__StreamDecoderWriteStatus write_callback( const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[] );
|
||||
virtual ::FLAC__StreamDecoderSeekStatus seek_callback( FLAC__uint64 absolute_byte_offset );
|
||||
virtual bool eof_callback();
|
||||
virtual void metadata_callback( const ::FLAC__StreamMetadata *metadata );
|
||||
void error_callback( ::FLAC__StreamDecoderErrorStatus status );
|
||||
|
||||
private:
|
||||
QByteArray m_outBuffer;
|
||||
|
||||
QMutex m_mutex;
|
||||
QByteArray m_buffer;
|
||||
|
||||
bool m_FLACRunning;
|
||||
bool m_finished;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,204 +0,0 @@
|
||||
#include "madtranscode.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
typedef struct audio_dither
|
||||
{
|
||||
mad_fixed_t error[3];
|
||||
mad_fixed_t random;
|
||||
} audio_dither;
|
||||
|
||||
|
||||
/* fast 32-bit pseudo-random number generator */
|
||||
/* code from madplay */
|
||||
static inline unsigned long prng( unsigned long state )
|
||||
{
|
||||
return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
|
||||
}
|
||||
|
||||
|
||||
/* dithers 24-bit output to 16 bits instead of simple rounding */
|
||||
/* code from madplay */
|
||||
static inline signed int dither( mad_fixed_t sample, audio_dither *dither )
|
||||
{
|
||||
unsigned int scalebits;
|
||||
mad_fixed_t output, mask, random;
|
||||
|
||||
enum
|
||||
{
|
||||
MIN = -MAD_F_ONE,
|
||||
MAX = MAD_F_ONE - 1
|
||||
};
|
||||
|
||||
/* noise shape */
|
||||
sample += dither->error[0] - dither->error[1] + dither->error[2];
|
||||
|
||||
dither->error[2] = dither->error[1];
|
||||
dither->error[1] = dither->error[0] / 2;
|
||||
|
||||
/* bias */
|
||||
output = sample + (1L << (MAD_F_FRACBITS + 1 - 16 - 1));
|
||||
|
||||
scalebits = MAD_F_FRACBITS + 1 - 16;
|
||||
mask = (1L << scalebits) - 1;
|
||||
|
||||
/* dither */
|
||||
random = prng(dither->random);
|
||||
output += (random & mask) - (dither->random & mask);
|
||||
|
||||
dither->random = random;
|
||||
|
||||
/* clip */
|
||||
/* TODO: better clipping function */
|
||||
if (sample >= MAD_F_ONE)
|
||||
sample = MAD_F_ONE - 1;
|
||||
else if (sample < -MAD_F_ONE)
|
||||
sample = -MAD_F_ONE;
|
||||
if (output >= MAD_F_ONE)
|
||||
output = MAD_F_ONE - 1;
|
||||
else if (output < -MAD_F_ONE)
|
||||
output = -MAD_F_ONE;
|
||||
|
||||
/* quantize */
|
||||
output &= ~mask;
|
||||
|
||||
/* error feedback */
|
||||
dither->error[0] = sample - output;
|
||||
|
||||
/* scale */
|
||||
return output >> scalebits;
|
||||
}
|
||||
|
||||
|
||||
MADTranscode::MADTranscode() :
|
||||
m_decodedBufferCapacity( 32 * 1024 ),
|
||||
m_mpegInitialised( false )
|
||||
{
|
||||
qDebug() << "Initialising MAD Transcoding";
|
||||
|
||||
mad_stream_init( &stream );
|
||||
mad_frame_init( &frame );
|
||||
mad_synth_init( &synth );
|
||||
timer = mad_timer_zero;
|
||||
last_timer = mad_timer_zero;
|
||||
}
|
||||
|
||||
|
||||
MADTranscode::~MADTranscode()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
|
||||
mad_synth_finish( &synth );
|
||||
mad_frame_finish( &frame );
|
||||
mad_stream_finish( &stream );
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MADTranscode::processData( const QByteArray &buffer, bool finish )
|
||||
{
|
||||
static audio_dither left_dither, right_dither;
|
||||
|
||||
int err = 0;
|
||||
m_encodedBuffer.append( buffer );
|
||||
|
||||
while ( err == 0 && ( m_encodedBuffer.count() >= MP3_BUFFER || finish ) )
|
||||
{
|
||||
mad_stream_buffer( &stream, (const unsigned char*)m_encodedBuffer.data(), m_encodedBuffer.count() );
|
||||
err = mad_frame_decode( &frame, &stream );
|
||||
|
||||
if ( stream.next_frame != 0 )
|
||||
{
|
||||
size_t r = stream.next_frame - stream.buffer;
|
||||
m_encodedBuffer.remove( 0, r );
|
||||
}
|
||||
|
||||
if ( err )
|
||||
{
|
||||
// if ( stream.error != MAD_ERROR_LOSTSYNC )
|
||||
// qDebug() << "libmad error:" << mad_stream_errorstr( &stream );
|
||||
|
||||
if ( !MAD_RECOVERABLE( stream.error ) )
|
||||
return;
|
||||
|
||||
err = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mad_timer_add( &timer, frame.header.duration );
|
||||
mad_synth_frame( &synth, &frame );
|
||||
|
||||
if ( !m_mpegInitialised )
|
||||
{
|
||||
long sampleRate = synth.pcm.samplerate;
|
||||
int channels = synth.pcm.channels;
|
||||
|
||||
qDebug() << "madTranscode( Samplerate:" << sampleRate << "- Channels:" << channels << ")";
|
||||
|
||||
m_mpegInitialised = true;
|
||||
emit streamInitialized( sampleRate, channels > 0 ? channels : 2 );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < synth.pcm.length; i++ )
|
||||
{
|
||||
union PCMDATA
|
||||
{
|
||||
short i;
|
||||
unsigned char b[2];
|
||||
} pcmData;
|
||||
|
||||
pcmData.i = dither( synth.pcm.samples[0][i], &left_dither );
|
||||
m_decodedBuffer.append( pcmData.b[0] );
|
||||
m_decodedBuffer.append( pcmData.b[1] );
|
||||
|
||||
if ( synth.pcm.channels == 2 )
|
||||
{
|
||||
pcmData.i = dither( synth.pcm.samples[1][i], &right_dither );
|
||||
m_decodedBuffer.append( pcmData.b[0] );
|
||||
m_decodedBuffer.append( pcmData.b[1] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( timer.seconds != last_timer.seconds )
|
||||
emit timeChanged( timer.seconds );
|
||||
|
||||
last_timer = timer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MADTranscode::onSeek( int seconds )
|
||||
{
|
||||
mad_timer_t t;
|
||||
t.seconds = seconds;
|
||||
t.fraction = 0;
|
||||
|
||||
timer = mad_timer_zero;
|
||||
mad_timer_add( &timer, t );
|
||||
|
||||
m_encodedBuffer.clear();
|
||||
m_decodedBuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MADTranscode::clearBuffers()
|
||||
{
|
||||
mad_synth_finish( &synth );
|
||||
mad_frame_finish( &frame );
|
||||
mad_stream_finish( &stream );
|
||||
|
||||
m_mpegInitialised = false;
|
||||
timer = mad_timer_zero;
|
||||
last_timer = mad_timer_zero;
|
||||
|
||||
m_encodedBuffer.clear();
|
||||
m_decodedBuffer.clear();
|
||||
|
||||
mad_stream_init( &stream );
|
||||
mad_frame_init( &frame );
|
||||
mad_synth_init( &synth );
|
||||
}
|
||||
|
@@ -1,65 +0,0 @@
|
||||
/*! \class MadTranscode
|
||||
\brief Transcoding plugin for MP3 streams, using libmad.
|
||||
*/
|
||||
|
||||
#ifndef MADTRANSCODE_H
|
||||
#define MADTRANSCODE_H
|
||||
|
||||
#include "transcodeinterface.h"
|
||||
|
||||
#include "mad.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
|
||||
#include "dllmacro.h"
|
||||
|
||||
#define MP3_BUFFER 32768
|
||||
#define MP3_BUFFER_PREFERRED 32768
|
||||
|
||||
class DLLEXPORT MADTranscode : public TranscodeInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MADTranscode();
|
||||
virtual ~MADTranscode();
|
||||
|
||||
const QStringList supportedTypes() const { QStringList l; l << "application/x-mp3" << "mp3"; return l; }
|
||||
|
||||
int needData() { return MP3_BUFFER - m_encodedBuffer.count(); }
|
||||
bool haveData() { return !m_decodedBuffer.isEmpty(); }
|
||||
|
||||
unsigned int preferredDataSize() { return MP3_BUFFER_PREFERRED; }
|
||||
|
||||
QByteArray data() { QByteArray b = m_decodedBuffer; m_decodedBuffer.clear(); return b; }
|
||||
|
||||
virtual void setBufferCapacity( int bytes ) { m_decodedBufferCapacity = bytes; }
|
||||
int bufferSize() { return m_decodedBuffer.size(); }
|
||||
|
||||
public slots:
|
||||
virtual void clearBuffers();
|
||||
virtual void onSeek( int seconds );
|
||||
virtual void processData( const QByteArray& data, bool finish );
|
||||
|
||||
signals:
|
||||
void streamInitialized( long sampleRate, int channels );
|
||||
void timeChanged( int seconds );
|
||||
|
||||
private:
|
||||
QByteArray m_encodedBuffer;
|
||||
QByteArray m_decodedBuffer;
|
||||
int m_decodedBufferCapacity;
|
||||
|
||||
bool m_mpegInitialised;
|
||||
struct mad_decoder decoder;
|
||||
struct mad_stream stream;
|
||||
struct mad_frame frame;
|
||||
struct mad_synth synth;
|
||||
mad_timer_t timer;
|
||||
mad_timer_t last_timer;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,263 +0,0 @@
|
||||
#include <QMutexLocker>
|
||||
#include <QStringList>
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
|
||||
#include "rtaudiooutput.h"
|
||||
|
||||
#define BUFFER_SIZE 512
|
||||
|
||||
int
|
||||
audioCallback( void *outputBuffer, void *inputBuffer, unsigned int bufferSize, double streamTime, RtAudioStreamStatus status, void* data_src )
|
||||
{
|
||||
RTAudioOutput* parent = (RTAudioOutput*)data_src;
|
||||
QMutexLocker locker( parent->mutex() );
|
||||
|
||||
char* buffer = (char*)outputBuffer;
|
||||
|
||||
if ( !buffer || bufferSize != BUFFER_SIZE )
|
||||
return 0;
|
||||
|
||||
int bufs = bufferSize * 2 * parent->sourceChannels();
|
||||
memset( buffer, 0, bufs );
|
||||
|
||||
if ( parent->buffer()->size() >= bufs && !parent->isPaused() )
|
||||
{
|
||||
// Apply volume scaling
|
||||
for ( int i = 0; i < bufs / 2; i++ )
|
||||
{
|
||||
union PCMDATA
|
||||
{
|
||||
short i;
|
||||
unsigned char b[2];
|
||||
} pcmData;
|
||||
|
||||
pcmData.b[0] = parent->buffer()->at( i * 2 );
|
||||
pcmData.b[1] = parent->buffer()->at( i * 2 + 1 );
|
||||
|
||||
float pcmValue = (float)pcmData.i * parent->volume();
|
||||
pcmData.i = (short)pcmValue;
|
||||
|
||||
buffer[i * 2] = pcmData.b[0];
|
||||
buffer[i * 2 + 1] = pcmData.b[1];
|
||||
}
|
||||
|
||||
parent->m_pcmCounter += bufs;
|
||||
parent->buffer()->remove( 0, bufs );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
RTAudioOutput::RTAudioOutput() :
|
||||
m_pcmCounter( 0 ),
|
||||
m_audio( new RtAudio() ),
|
||||
m_bufferEmpty( true ),
|
||||
m_volume( 0.75 ),
|
||||
m_paused( false ),
|
||||
m_playing( false ),
|
||||
m_bps( -1 )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << m_audio->getCurrentApi();
|
||||
devices();
|
||||
}
|
||||
|
||||
|
||||
RTAudioOutput::~RTAudioOutput()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
stopPlayback();
|
||||
}
|
||||
|
||||
|
||||
QStringList
|
||||
RTAudioOutput::soundSystems()
|
||||
{
|
||||
QStringList l;
|
||||
|
||||
#ifdef WIN32
|
||||
l << "DirectSound";
|
||||
#endif
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
l << "Alsa";
|
||||
#endif
|
||||
|
||||
#ifdef Q_WS_MAC
|
||||
l << "CoreAudio";
|
||||
#endif
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
QStringList
|
||||
RTAudioOutput::devices()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QStringList l;
|
||||
|
||||
try
|
||||
{
|
||||
qDebug() << "Device nums:" << m_audio->getDeviceCount();
|
||||
|
||||
for ( unsigned int i = 0; i < m_audio->getDeviceCount(); i++ )
|
||||
{
|
||||
RtAudio::DeviceInfo info;
|
||||
info = m_audio->getDeviceInfo( i );
|
||||
qDebug() << "Device found:" << i << QString::fromStdString( info.name ) << info.outputChannels << info.duplexChannels << info.isDefaultOutput;
|
||||
|
||||
if ( info.outputChannels > 0 )
|
||||
l << QString::fromStdString( info.name ); // FIXME make it utf8 compatible
|
||||
}
|
||||
}
|
||||
catch ( RtError &error )
|
||||
{
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
RTAudioOutput::startPlayback()
|
||||
{
|
||||
qDebug () << Q_FUNC_INFO;
|
||||
|
||||
if ( m_audio->isStreamOpen() )
|
||||
{
|
||||
m_audio->startStream();
|
||||
m_playing = true;
|
||||
}
|
||||
|
||||
return m_audio->isStreamOpen();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RTAudioOutput::stopPlayback()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
delete m_audio; // FIXME
|
||||
m_audio = new RtAudio();
|
||||
m_buffer.clear();
|
||||
m_paused = false;
|
||||
m_playing = false;
|
||||
m_bps = -1;
|
||||
m_pcmCounter = 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RTAudioOutput::initAudio( long sampleRate, int channels )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << sampleRate << channels;
|
||||
QMutexLocker locker( &m_mutex );
|
||||
try
|
||||
{
|
||||
delete m_audio;
|
||||
m_audio = new RtAudio();
|
||||
m_bps = sampleRate * channels * 2;
|
||||
m_pcmCounter = 0;
|
||||
|
||||
RtAudio::StreamParameters parameters;
|
||||
parameters.deviceId = m_audio->getDefaultOutputDevice();
|
||||
parameters.nChannels = channels;
|
||||
parameters.firstChannel = 0;
|
||||
unsigned int bufferFrames = BUFFER_SIZE;
|
||||
|
||||
RtAudio::StreamOptions options;
|
||||
options.numberOfBuffers = 32;
|
||||
//options.flags = RTAUDIO_SCHEDULE_REALTIME;
|
||||
|
||||
m_sourceChannels = channels;
|
||||
m_buffer.clear();
|
||||
|
||||
/* if ( m_audio->isStreamRunning() )
|
||||
m_audio->abortStream();
|
||||
|
||||
if ( m_audio->isStreamOpen() )
|
||||
m_audio->closeStream();*/
|
||||
|
||||
m_audio->openStream( ¶meters, NULL, RTAUDIO_SINT16, sampleRate, &bufferFrames, &audioCallback, this, &options );
|
||||
}
|
||||
catch ( RtError &error )
|
||||
{
|
||||
qDebug() << "Starting stream failed. RtAudio error type: " << error.getType();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
RTAudioOutput::needData()
|
||||
{
|
||||
if ( m_buffer.isEmpty() && !m_bufferEmpty )
|
||||
{
|
||||
m_bufferEmpty = true;
|
||||
emit bufferEmpty();
|
||||
}
|
||||
|
||||
return ( m_buffer.size() < 65535 ); // FIXME constant value
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RTAudioOutput::processData( const QByteArray &buffer )
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_buffer.append( buffer );
|
||||
if ( m_bufferEmpty && !buffer.isEmpty() )
|
||||
{
|
||||
m_bufferEmpty = false;
|
||||
emit bufferFull();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
RTAudioOutput::clearBuffers()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_buffer.clear();
|
||||
m_bufferEmpty = true;
|
||||
emit bufferEmpty();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
RTAudioOutput::internalSoundCardID( int settingsID )
|
||||
{
|
||||
if ( settingsID < 0 )
|
||||
settingsID = 0;
|
||||
|
||||
try
|
||||
{
|
||||
int card = 0;
|
||||
|
||||
for ( unsigned int i = 1; i <= m_audio->getDeviceCount(); i++ )
|
||||
{
|
||||
RtAudio::DeviceInfo info;
|
||||
info = m_audio->getDeviceInfo( i );
|
||||
if ( info.outputChannels > 0 )
|
||||
{
|
||||
if ( card++ == settingsID )
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( RtError &error )
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef Q_WS_MAC
|
||||
return 3; // FIXME?
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
@@ -1,71 +0,0 @@
|
||||
#ifndef RTAUDIOPLAYBACK_H
|
||||
#define RTAUDIOPLAYBACK_H
|
||||
|
||||
#include "RtAudio.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
|
||||
class RTAudioOutput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RTAudioOutput();
|
||||
~RTAudioOutput();
|
||||
|
||||
void initAudio( long sampleRate, int channels );
|
||||
|
||||
float volume() { return m_volume; }
|
||||
bool isPaused() { return m_paused; }
|
||||
virtual bool isPlaying() { return m_playing; }
|
||||
|
||||
bool haveData() { return m_buffer.length() > 2048; }
|
||||
bool needData();
|
||||
void processData( const QByteArray &buffer );
|
||||
|
||||
QStringList soundSystems();
|
||||
QStringList devices();
|
||||
int sourceChannels() { return m_sourceChannels; }
|
||||
|
||||
QMutex* mutex() { return &m_mutex; }
|
||||
QByteArray* buffer() { return &m_buffer; }
|
||||
|
||||
int m_pcmCounter;
|
||||
|
||||
public slots:
|
||||
void clearBuffers();
|
||||
|
||||
bool startPlayback();
|
||||
void stopPlayback();
|
||||
|
||||
void pause() { m_paused = true; }
|
||||
void resume() { m_paused = false; }
|
||||
|
||||
void setVolume( int volume ) { m_volume = ((float)(volume)) / (float)100.0; emit volumeChanged( m_volume ); }
|
||||
virtual void triggerTimers() { if ( m_bps > 0 ) emit timeElapsed( m_pcmCounter / m_bps ); else emit timeElapsed( 0 ); }
|
||||
|
||||
signals:
|
||||
void bufferEmpty();
|
||||
void bufferFull();
|
||||
|
||||
void volumeChanged( float volume );
|
||||
void timeElapsed( unsigned int seconds );
|
||||
|
||||
private:
|
||||
RtAudio *m_audio;
|
||||
bool m_bufferEmpty;
|
||||
|
||||
float m_volume;
|
||||
QByteArray m_buffer;
|
||||
QMutex m_mutex;
|
||||
|
||||
int m_sourceChannels;
|
||||
bool m_paused;
|
||||
bool m_playing;
|
||||
int m_bps;
|
||||
|
||||
int internalSoundCardID( int settingsID );
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,36 +0,0 @@
|
||||
#ifndef TRANSCODEINTERFACE_H
|
||||
#define TRANSCODEINTERFACE_H
|
||||
|
||||
#include <QStringList>
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
|
||||
#include "dllmacro.h"
|
||||
|
||||
class DLLEXPORT TranscodeInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~TranscodeInterface() {}
|
||||
|
||||
virtual const QStringList supportedTypes() const = 0;
|
||||
|
||||
virtual int needData() = 0;
|
||||
virtual bool haveData() = 0;
|
||||
|
||||
virtual unsigned int preferredDataSize() = 0;
|
||||
|
||||
virtual QByteArray data() = 0;
|
||||
|
||||
// virtual void setBufferCapacity( int bytes ) = 0;
|
||||
// virtual int bufferSize() = 0;
|
||||
|
||||
public slots:
|
||||
virtual void clearBuffers() = 0;
|
||||
virtual void onSeek( int seconds ) = 0;
|
||||
virtual void processData( const QByteArray& data, bool finish ) = 0;
|
||||
};
|
||||
|
||||
#endif
|
@@ -1,120 +0,0 @@
|
||||
#include "vorbistranscode.h"
|
||||
|
||||
|
||||
size_t
|
||||
vorbis_read( void* data_ptr, size_t byteSize, size_t sizeToRead, void* data_src )
|
||||
{
|
||||
VorbisTranscode* parent = (VorbisTranscode*)data_src;
|
||||
QMutexLocker locker( parent->mutex() );
|
||||
|
||||
int r = byteSize * sizeToRead;
|
||||
if ( r > parent->buffer()->size() )
|
||||
r = parent->buffer()->size();
|
||||
|
||||
memcpy( data_ptr, (char*)parent->buffer()->data(), r );
|
||||
parent->buffer()->remove( 0, r );
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
vorbis_seek( void* data_src, ogg_int64_t offset, int origin )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
vorbis_close( void* data_src )
|
||||
{
|
||||
// done ;-)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long
|
||||
vorbis_tell( void* data_src )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
VorbisTranscode::VorbisTranscode()
|
||||
: m_vorbisInit( false )
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
|
||||
VorbisTranscode::~VorbisTranscode()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VorbisTranscode::onSeek( int seconds )
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_buffer.clear();
|
||||
m_outBuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VorbisTranscode::clearBuffers()
|
||||
{
|
||||
QMutexLocker locker( &m_mutex );
|
||||
|
||||
m_vorbisInit = false;
|
||||
m_buffer.clear();
|
||||
m_outBuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VorbisTranscode::processData( const QByteArray& data, bool )
|
||||
{
|
||||
m_mutex.lock();
|
||||
m_buffer.append( data );
|
||||
m_mutex.unlock();
|
||||
|
||||
if ( !m_vorbisInit && m_buffer.size() >= OGG_BUFFER )
|
||||
{
|
||||
ov_callbacks oggCallbacks;
|
||||
|
||||
oggCallbacks.read_func = vorbis_read;
|
||||
oggCallbacks.close_func = vorbis_close;
|
||||
oggCallbacks.seek_func = vorbis_seek;
|
||||
oggCallbacks.tell_func = vorbis_tell;
|
||||
|
||||
ov_open_callbacks( this, &m_vorbisFile, 0, 0, oggCallbacks );
|
||||
m_vorbisInit = true;
|
||||
|
||||
// Try to determine samplerate
|
||||
vorbis_info* vi = ov_info( &m_vorbisFile, -1 );
|
||||
qDebug() << "vorbisTranscode( Samplerate:" << vi->rate << "Channels:" << vi->channels << ")";
|
||||
|
||||
emit streamInitialized( vi->rate, vi->channels );
|
||||
}
|
||||
|
||||
long result = 1;
|
||||
int currentSection = 0;
|
||||
|
||||
while ( m_buffer.size() >= OGG_BUFFER && result > 0 )
|
||||
{
|
||||
char tempBuffer[16384];
|
||||
result = ov_read( &m_vorbisFile, tempBuffer, sizeof( tempBuffer ), 0, 2, 1, ¤tSection );
|
||||
|
||||
if ( result > 0 )
|
||||
{
|
||||
for ( int i = 0; i < ( result / 2 ); i++ )
|
||||
{
|
||||
m_outBuffer.append( tempBuffer[i * 2] );
|
||||
m_outBuffer.append( tempBuffer[i * 2 + 1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,63 +0,0 @@
|
||||
/*! \class VorbisTranscode
|
||||
\brief Transcoding plugin for OGG/Vorbis streams.
|
||||
*/
|
||||
|
||||
#ifndef VORBIS_TRANSCODE_H
|
||||
#define VORBIS_TRANSCODE_H
|
||||
|
||||
#include "transcodeinterface.h"
|
||||
|
||||
#include <vorbis/codec.h>
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
|
||||
#include "dllmacro.h"
|
||||
|
||||
// Must not be smaller than 8500 bytes!
|
||||
#define OGG_BUFFER 8500
|
||||
#define OGG_BUFFER_PREFERRED 32768
|
||||
|
||||
class DLLEXPORT VorbisTranscode : public TranscodeInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
VorbisTranscode();
|
||||
~VorbisTranscode();
|
||||
|
||||
const QStringList supportedTypes() const { QStringList l; l << "application/ogg" << "ogg"; return l; }
|
||||
|
||||
int needData() { return OGG_BUFFER - m_buffer.count(); }
|
||||
bool haveData() { return !m_outBuffer.isEmpty(); }
|
||||
|
||||
unsigned int preferredDataSize() { return OGG_BUFFER_PREFERRED; }
|
||||
|
||||
QByteArray data() { QByteArray b = m_outBuffer; m_outBuffer.clear(); return b; }
|
||||
|
||||
QMutex* mutex() { return &m_mutex; }
|
||||
QByteArray* buffer() { return &m_buffer; }
|
||||
|
||||
public slots:
|
||||
void clearBuffers();
|
||||
void onSeek( int seconds );
|
||||
void processData( const QByteArray& data, bool finish );
|
||||
|
||||
signals:
|
||||
void streamInitialized( long sampleRate, int channels );
|
||||
void timeChanged( int seconds );
|
||||
|
||||
private:
|
||||
QByteArray m_outBuffer;
|
||||
|
||||
QMutex m_mutex;
|
||||
QByteArray m_buffer;
|
||||
|
||||
OggVorbis_File m_vorbisFile;
|
||||
bool m_vorbisInit;
|
||||
};
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user