2014-09-04 07:33:44 +00:00
/*
This file is part of Telegram Desktop ,
2014-12-01 10:47:38 +00:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-09-04 07:33:44 +00:00
Telegram Desktop 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 3 of the License , or
( at your option ) any later version .
It 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 .
2015-10-03 13:16:42 +00:00
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
2014-09-04 07:33:44 +00:00
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2016-02-08 10:56:18 +00:00
Copyright ( c ) 2014 - 2016 John Preston , https : //desktop.telegram.org
2014-09-04 07:33:44 +00:00
*/
# include "stdafx.h"
2016-03-24 12:57:10 +00:00
2014-09-04 07:33:44 +00:00
# include "audio.h"
# include <AL/al.h>
# include <AL/alc.h>
2015-05-24 17:58:39 +00:00
2015-06-03 12:18:46 +00:00
# define AL_ALEXT_PROTOTYPES
# include <AL/alext.h>
2015-05-30 16:30:47 +00:00
extern " C " {
2016-03-24 12:57:10 +00:00
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libavutil/opt.h>
# include <libswresample/swresample.h>
2015-05-30 16:30:47 +00:00
2016-03-24 12:57:10 +00:00
# ifdef Q_OS_MAC
2015-05-30 16:30:47 +00:00
# include <iconv.h>
# undef iconv_open
# undef iconv
# undef iconv_close
iconv_t iconv_open ( const char * tocode , const char * fromcode ) {
return libiconv_open ( tocode , fromcode ) ;
}
size_t iconv ( iconv_t cd , char * * inbuf , size_t * inbytesleft , char * * outbuf , size_t * outbytesleft ) {
return libiconv ( cd , inbuf , inbytesleft , outbuf , outbytesleft ) ;
}
int iconv_close ( iconv_t cd ) {
return libiconv_close ( cd ) ;
}
2016-03-24 12:57:10 +00:00
# endif // Q_OS_MAC
2015-05-30 16:30:47 +00:00
2016-03-24 12:57:10 +00:00
} // extern "C"
2015-05-30 16:30:47 +00:00
2014-09-04 07:33:44 +00:00
namespace {
ALCdevice * audioDevice = 0 ;
ALCcontext * audioContext = 0 ;
ALuint notifySource = 0 ;
ALuint notifyBuffer = 0 ;
2015-05-29 18:52:43 +00:00
2015-06-30 21:07:05 +00:00
uint64 notifyLengthMs = 0 ;
2015-05-29 18:52:43 +00:00
QMutex playerMutex ;
AudioPlayer * player = 0 ;
2015-06-30 21:07:05 +00:00
float64 suppressAllGain = 1. , suppressSongGain = 1. ;
2015-05-29 18:52:43 +00:00
AudioCapture * capture = 0 ;
2014-09-04 07:33:44 +00:00
}
bool _checkALCError ( ) {
ALenum errCode ;
if ( ( errCode = alcGetError ( audioDevice ) ) ! = ALC_NO_ERROR ) {
2015-05-29 18:52:43 +00:00
LOG ( ( " Audio Error: (alc) %1, %2 " ) . arg ( errCode ) . arg ( ( const char * ) alcGetString ( audioDevice , errCode ) ) ) ;
return false ;
}
return true ;
}
bool _checkCaptureError ( ALCdevice * device ) {
ALenum errCode ;
if ( ( errCode = alcGetError ( device ) ) ! = ALC_NO_ERROR ) {
LOG ( ( " Audio Error: (capture) %1, %2 " ) . arg ( errCode ) . arg ( ( const char * ) alcGetString ( audioDevice , errCode ) ) ) ;
2014-09-04 07:33:44 +00:00
return false ;
}
return true ;
}
bool _checkALError ( ) {
ALenum errCode ;
if ( ( errCode = alGetError ( ) ) ! = AL_NO_ERROR ) {
2015-05-29 18:52:43 +00:00
LOG ( ( " Audio Error: (al) %1, %2 " ) . arg ( errCode ) . arg ( ( const char * ) alGetString ( errCode ) ) ) ;
2014-09-04 07:33:44 +00:00
return false ;
}
return true ;
}
2015-06-30 21:07:05 +00:00
Q_DECLARE_METATYPE ( AudioMsgId ) ;
Q_DECLARE_METATYPE ( SongMsgId ) ;
2016-02-12 16:35:06 +00:00
Q_DECLARE_METATYPE ( VoiceWaveform ) ;
2014-09-04 07:33:44 +00:00
void audioInit ( ) {
2015-05-29 18:52:43 +00:00
if ( ! capture ) {
capture = new AudioCapture ( ) ;
2015-06-01 10:58:46 +00:00
cSetHasAudioCapture ( capture - > check ( ) ) ;
2015-05-29 18:52:43 +00:00
}
2014-09-04 07:33:44 +00:00
if ( audioDevice ) return ;
2015-05-29 18:52:43 +00:00
audioDevice = alcOpenDevice ( 0 ) ;
2014-09-04 07:33:44 +00:00
if ( ! audioDevice ) {
LOG ( ( " Audio Error: default sound device not present. " ) ) ;
return ;
}
2016-01-05 06:59:57 +00:00
2014-09-04 07:33:44 +00:00
ALCint attributes [ ] = { ALC_STEREO_SOURCES , 8 , 0 } ;
audioContext = alcCreateContext ( audioDevice , attributes ) ;
alcMakeContextCurrent ( audioContext ) ;
if ( ! _checkALCError ( ) ) return audioFinish ( ) ;
ALfloat v [ ] = { 0.f , 0.f , - 1.f , 0.f , 1.f , 0.f } ;
alListener3f ( AL_POSITION , 0.f , 0.f , 0.f ) ;
alListener3f ( AL_VELOCITY , 0.f , 0.f , 0.f ) ;
alListenerfv ( AL_ORIENTATION , v ) ;
alDistanceModel ( AL_NONE ) ;
alGenSources ( 1 , & notifySource ) ;
alSourcef ( notifySource , AL_PITCH , 1.f ) ;
alSourcef ( notifySource , AL_GAIN , 1.f ) ;
alSource3f ( notifySource , AL_POSITION , 0 , 0 , 0 ) ;
alSource3f ( notifySource , AL_VELOCITY , 0 , 0 , 0 ) ;
alSourcei ( notifySource , AL_LOOPING , 0 ) ;
alGenBuffers ( 1 , & notifyBuffer ) ;
if ( ! _checkALError ( ) ) return audioFinish ( ) ;
QFile notify ( st : : newMsgSound ) ;
if ( ! notify . open ( QIODevice : : ReadOnly ) ) return audioFinish ( ) ;
QByteArray blob = notify . readAll ( ) ;
const char * data = blob . constData ( ) ;
if ( blob . size ( ) < 44 ) return audioFinish ( ) ;
if ( * ( ( const uint32 * ) ( data + 0 ) ) ! = 0x46464952 ) return audioFinish ( ) ; // ChunkID - "RIFF"
2014-09-04 12:23:28 +00:00
if ( * ( ( const uint32 * ) ( data + 4 ) ) ! = uint32 ( blob . size ( ) - 8 ) ) return audioFinish ( ) ; // ChunkSize
2014-09-04 07:33:44 +00:00
if ( * ( ( const uint32 * ) ( data + 8 ) ) ! = 0x45564157 ) return audioFinish ( ) ; // Format - "WAVE"
if ( * ( ( const uint32 * ) ( data + 12 ) ) ! = 0x20746d66 ) return audioFinish ( ) ; // Subchunk1ID - "fmt "
uint32 subchunk1Size = * ( ( const uint32 * ) ( data + 16 ) ) , extra = subchunk1Size - 16 ;
2014-09-04 12:23:28 +00:00
if ( subchunk1Size < 16 | | ( extra & & extra < 2 ) ) return audioFinish ( ) ;
2014-09-04 07:33:44 +00:00
if ( * ( ( const uint16 * ) ( data + 20 ) ) ! = 1 ) return audioFinish ( ) ; // AudioFormat - PCM (1)
uint16 numChannels = * ( ( const uint16 * ) ( data + 22 ) ) ;
if ( numChannels ! = 1 & & numChannels ! = 2 ) return audioFinish ( ) ;
uint32 sampleRate = * ( ( const uint32 * ) ( data + 24 ) ) ;
uint32 byteRate = * ( ( const uint32 * ) ( data + 28 ) ) ;
uint16 blockAlign = * ( ( const uint16 * ) ( data + 32 ) ) ;
uint16 bitsPerSample = * ( ( const uint16 * ) ( data + 34 ) ) ;
if ( bitsPerSample % 8 ) return audioFinish ( ) ;
uint16 bytesPerSample = bitsPerSample / 8 ;
if ( bytesPerSample ! = 1 & & bytesPerSample ! = 2 ) return audioFinish ( ) ;
if ( blockAlign ! = numChannels * bytesPerSample ) return audioFinish ( ) ;
if ( byteRate ! = sampleRate * blockAlign ) return audioFinish ( ) ;
if ( extra ) {
uint16 extraSize = * ( ( const uint16 * ) ( data + 36 ) ) ;
2014-09-04 19:24:03 +00:00
if ( uint32 ( extraSize + 2 ) ! = extra ) return audioFinish ( ) ;
2014-09-04 12:23:28 +00:00
if ( uint32 ( blob . size ( ) ) < 44 + extra ) return audioFinish ( ) ;
2014-09-04 07:33:44 +00:00
}
if ( * ( ( const uint32 * ) ( data + extra + 36 ) ) ! = 0x61746164 ) return audioFinish ( ) ; // Subchunk2ID - "data"
uint32 subchunk2Size = * ( ( const uint32 * ) ( data + extra + 40 ) ) ;
if ( subchunk2Size % ( numChannels * bytesPerSample ) ) return audioFinish ( ) ;
uint32 numSamples = subchunk2Size / ( numChannels * bytesPerSample ) ;
2014-09-04 12:23:28 +00:00
if ( uint32 ( blob . size ( ) ) < 44 + extra + subchunk2Size ) return audioFinish ( ) ;
2014-09-04 07:33:44 +00:00
data + = 44 + extra ;
ALenum format = 0 ;
switch ( bytesPerSample ) {
case 1 :
switch ( numChannels ) {
case 1 : format = AL_FORMAT_MONO8 ; break ;
case 2 : format = AL_FORMAT_STEREO8 ; break ;
}
break ;
case 2 :
switch ( numChannels ) {
case 1 : format = AL_FORMAT_MONO16 ; break ;
case 2 : format = AL_FORMAT_STEREO16 ; break ;
}
break ;
}
if ( ! format ) return audioFinish ( ) ;
2015-06-30 21:07:05 +00:00
int32 addBytes = ( sampleRate * 15 / 100 ) * bytesPerSample * numChannels ; // add 150ms of silence
QByteArray fullData ( addBytes + subchunk2Size , ( bytesPerSample = = 1 ) ? 128 : 0 ) ;
memcpy ( fullData . data ( ) + addBytes , data , subchunk2Size ) ;
alBufferData ( notifyBuffer , format , fullData . constData ( ) , fullData . size ( ) , sampleRate ) ;
2014-09-04 07:33:44 +00:00
alSourcei ( notifySource , AL_BUFFER , notifyBuffer ) ;
2015-06-30 21:07:05 +00:00
notifyLengthMs = ( numSamples * 1000ULL / sampleRate ) ;
2014-09-04 07:33:44 +00:00
if ( ! _checkALError ( ) ) return audioFinish ( ) ;
2015-06-30 21:07:05 +00:00
qRegisterMetaType < AudioMsgId > ( ) ;
qRegisterMetaType < SongMsgId > ( ) ;
2016-02-12 16:35:06 +00:00
qRegisterMetaType < VoiceWaveform > ( ) ;
2015-06-30 21:07:05 +00:00
2015-05-29 18:52:43 +00:00
player = new AudioPlayer ( ) ;
2015-06-03 12:18:46 +00:00
alcDevicePauseSOFT ( audioDevice ) ;
2015-05-24 17:58:39 +00:00
2015-06-01 10:58:46 +00:00
cSetHasAudioPlayer ( true ) ;
2014-09-04 07:33:44 +00:00
}
void audioPlayNotify ( ) {
2015-05-29 18:52:43 +00:00
if ( ! audioPlayer ( ) ) return ;
2014-09-04 07:33:44 +00:00
2015-06-03 12:18:46 +00:00
audioPlayer ( ) - > resumeDevice ( ) ;
2014-09-04 07:33:44 +00:00
alSourcePlay ( notifySource ) ;
2015-06-30 21:07:05 +00:00
emit audioPlayer ( ) - > suppressAll ( ) ;
2015-05-29 18:52:43 +00:00
emit audioPlayer ( ) - > faderOnTimer ( ) ;
2014-09-04 07:33:44 +00:00
}
2016-03-25 20:46:35 +00:00
// can be called at any moment when audio error
2014-09-04 07:33:44 +00:00
void audioFinish ( ) {
2015-05-29 18:52:43 +00:00
if ( player ) {
2016-03-25 20:46:35 +00:00
delete player ;
player = nullptr ;
2015-05-29 18:52:43 +00:00
}
if ( capture ) {
2016-03-25 20:46:35 +00:00
delete capture ;
capture = nullptr ;
2014-09-04 07:33:44 +00:00
}
alSourceStop ( notifySource ) ;
if ( alIsBuffer ( notifyBuffer ) ) {
alDeleteBuffers ( 1 , & notifyBuffer ) ;
notifyBuffer = 0 ;
}
if ( alIsSource ( notifySource ) ) {
alDeleteSources ( 1 , & notifySource ) ;
notifySource = 0 ;
}
if ( audioContext ) {
2016-03-20 08:16:35 +00:00
alcMakeContextCurrent ( nullptr ) ;
2014-09-04 07:33:44 +00:00
alcDestroyContext ( audioContext ) ;
2016-03-20 08:16:35 +00:00
audioContext = nullptr ;
2014-09-04 07:33:44 +00:00
}
if ( audioDevice ) {
alcCloseDevice ( audioDevice ) ;
2016-03-20 08:16:35 +00:00
audioDevice = nullptr ;
2014-09-04 07:33:44 +00:00
}
2015-06-01 10:58:46 +00:00
cSetHasAudioCapture ( false ) ;
cSetHasAudioPlayer ( false ) ;
2014-09-04 07:33:44 +00:00
}
2015-11-24 16:19:18 +00:00
void AudioPlayer : : Msg : : clearData ( ) {
2015-11-26 17:34:52 +00:00
file = FileLocation ( ) ;
2015-11-24 16:19:18 +00:00
data = QByteArray ( ) ;
position = duration = 0 ;
frequency = AudioVoiceMsgFrequency ;
skipStart = skipEnd = 0 ;
loading = false ;
started = 0 ;
state = AudioPlayerStopped ;
if ( alIsSource ( source ) ) {
alSourceStop ( source ) ;
}
for ( int32 i = 0 ; i < 3 ; + + i ) {
if ( samplesCount [ i ] ) {
alSourceUnqueueBuffers ( source , 1 , buffers + i ) ;
samplesCount [ i ] = 0 ;
}
}
nextBuffer = 0 ;
}
2015-06-30 21:07:05 +00:00
AudioPlayer : : AudioPlayer ( ) : _audioCurrent ( 0 ) , _songCurrent ( 0 ) ,
2015-05-29 18:52:43 +00:00
_fader ( new AudioPlayerFader ( & _faderThread ) ) ,
_loader ( new AudioPlayerLoaders ( & _loaderThread ) ) {
2014-09-04 07:33:44 +00:00
connect ( this , SIGNAL ( faderOnTimer ( ) ) , _fader , SLOT ( onTimer ( ) ) ) ;
2015-06-30 21:07:05 +00:00
connect ( this , SIGNAL ( suppressSong ( ) ) , _fader , SLOT ( onSuppressSong ( ) ) ) ;
connect ( this , SIGNAL ( unsuppressSong ( ) ) , _fader , SLOT ( onUnsuppressSong ( ) ) ) ;
connect ( this , SIGNAL ( suppressAll ( ) ) , _fader , SLOT ( onSuppressAll ( ) ) ) ;
2015-07-03 08:47:16 +00:00
connect ( this , SIGNAL ( songVolumeChanged ( ) ) , _fader , SLOT ( onSongVolumeChanged ( ) ) ) ;
connect ( this , SIGNAL ( loaderOnStart ( const AudioMsgId & , qint64 ) ) , _loader , SLOT ( onStart ( const AudioMsgId & , qint64 ) ) ) ;
connect ( this , SIGNAL ( loaderOnStart ( const SongMsgId & , qint64 ) ) , _loader , SLOT ( onStart ( const SongMsgId & , qint64 ) ) ) ;
2015-06-30 21:07:05 +00:00
connect ( this , SIGNAL ( loaderOnCancel ( const AudioMsgId & ) ) , _loader , SLOT ( onCancel ( const AudioMsgId & ) ) ) ;
connect ( this , SIGNAL ( loaderOnCancel ( const SongMsgId & ) ) , _loader , SLOT ( onCancel ( const SongMsgId & ) ) ) ;
2014-09-04 07:33:44 +00:00
connect ( & _faderThread , SIGNAL ( started ( ) ) , _fader , SLOT ( onInit ( ) ) ) ;
connect ( & _loaderThread , SIGNAL ( started ( ) ) , _loader , SLOT ( onInit ( ) ) ) ;
connect ( & _faderThread , SIGNAL ( finished ( ) ) , _fader , SLOT ( deleteLater ( ) ) ) ;
connect ( & _loaderThread , SIGNAL ( finished ( ) ) , _loader , SLOT ( deleteLater ( ) ) ) ;
connect ( _loader , SIGNAL ( needToCheck ( ) ) , _fader , SLOT ( onTimer ( ) ) ) ;
2015-06-30 21:07:05 +00:00
connect ( _loader , SIGNAL ( error ( const AudioMsgId & ) ) , this , SLOT ( onError ( const AudioMsgId & ) ) ) ;
connect ( _loader , SIGNAL ( error ( const SongMsgId & ) ) , this , SLOT ( onError ( const SongMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( needToPreload ( const AudioMsgId & ) ) , _loader , SLOT ( onLoad ( const AudioMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( needToPreload ( const SongMsgId & ) ) , _loader , SLOT ( onLoad ( const SongMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( playPositionUpdated ( const AudioMsgId & ) ) , this , SIGNAL ( updated ( const AudioMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( playPositionUpdated ( const SongMsgId & ) ) , this , SIGNAL ( updated ( const SongMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( audioStopped ( const AudioMsgId & ) ) , this , SLOT ( onStopped ( const AudioMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( audioStopped ( const SongMsgId & ) ) , this , SLOT ( onStopped ( const SongMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( error ( const AudioMsgId & ) ) , this , SLOT ( onError ( const AudioMsgId & ) ) ) ;
connect ( _fader , SIGNAL ( error ( const SongMsgId & ) ) , this , SLOT ( onError ( const SongMsgId & ) ) ) ;
2015-07-03 08:47:16 +00:00
connect ( this , SIGNAL ( stoppedOnError ( const AudioMsgId & ) ) , this , SIGNAL ( stopped ( const AudioMsgId & ) ) , Qt : : QueuedConnection ) ;
connect ( this , SIGNAL ( stoppedOnError ( const SongMsgId & ) ) , this , SIGNAL ( stopped ( const SongMsgId & ) ) , Qt : : QueuedConnection ) ;
2014-09-04 07:33:44 +00:00
_loaderThread . start ( ) ;
_faderThread . start ( ) ;
}
2015-05-29 18:52:43 +00:00
AudioPlayer : : ~ AudioPlayer ( ) {
2014-09-04 07:33:44 +00:00
{
2015-05-29 18:52:43 +00:00
QMutexLocker lock ( & playerMutex ) ;
player = 0 ;
2014-09-04 07:33:44 +00:00
}
for ( int32 i = 0 ; i < AudioVoiceMsgSimultaneously ; + + i ) {
2015-06-30 21:07:05 +00:00
alSourceStop ( _audioData [ i ] . source ) ;
if ( alIsBuffer ( _audioData [ i ] . buffers [ 0 ] ) ) {
alDeleteBuffers ( 3 , _audioData [ i ] . buffers ) ;
for ( int32 j = 0 ; j < 3 ; + + j ) {
_audioData [ i ] . buffers [ j ] = _audioData [ i ] . samplesCount [ j ] = 0 ;
}
}
if ( alIsSource ( _audioData [ i ] . source ) ) {
alDeleteSources ( 1 , & _audioData [ i ] . source ) ;
_audioData [ i ] . source = 0 ;
}
}
for ( int32 i = 0 ; i < AudioSongSimultaneously ; + + i ) {
alSourceStop ( _songData [ i ] . source ) ;
if ( alIsBuffer ( _songData [ i ] . buffers [ 0 ] ) ) {
alDeleteBuffers ( 3 , _songData [ i ] . buffers ) ;
2014-09-04 07:33:44 +00:00
for ( int32 j = 0 ; j < 3 ; + + j ) {
2015-06-30 21:07:05 +00:00
_songData [ i ] . buffers [ j ] = _songData [ i ] . samplesCount [ j ] = 0 ;
2014-09-04 07:33:44 +00:00
}
}
2015-06-30 21:07:05 +00:00
if ( alIsSource ( _songData [ i ] . source ) ) {
alDeleteSources ( 1 , & _songData [ i ] . source ) ;
_songData [ i ] . source = 0 ;
2014-09-04 07:33:44 +00:00
}
}
_faderThread . quit ( ) ;
_loaderThread . quit ( ) ;
_faderThread . wait ( ) ;
_loaderThread . wait ( ) ;
}
2015-06-30 21:07:05 +00:00
void AudioPlayer : : onError ( const AudioMsgId & audio ) {
2015-07-03 08:47:16 +00:00
emit stoppedOnError ( audio ) ;
2015-06-30 21:07:05 +00:00
emit unsuppressSong ( ) ;
}
void AudioPlayer : : onError ( const SongMsgId & song ) {
2015-07-03 08:47:16 +00:00
emit stoppedOnError ( song ) ;
2015-06-30 21:07:05 +00:00
}
void AudioPlayer : : onStopped ( const AudioMsgId & audio ) {
2014-09-04 07:33:44 +00:00
emit stopped ( audio ) ;
2015-06-30 21:07:05 +00:00
emit unsuppressSong ( ) ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
void AudioPlayer : : onStopped ( const SongMsgId & song ) {
emit stopped ( song ) ;
}
bool AudioPlayer : : updateCurrentStarted ( MediaOverviewType type , int32 pos ) {
Msg * data = 0 ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : data = & _audioData [ _audioCurrent ] ; break ;
case OverviewFiles : data = & _songData [ _songCurrent ] ; break ;
2015-06-30 21:07:05 +00:00
}
if ( ! data ) return false ;
2014-09-04 07:33:44 +00:00
if ( pos < 0 ) {
2015-06-30 21:07:05 +00:00
if ( alIsSource ( data - > source ) ) {
alGetSourcei ( data - > source , AL_SAMPLE_OFFSET , & pos ) ;
2014-09-04 07:33:44 +00:00
} else {
pos = 0 ;
}
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) {
setStoppedState ( data , AudioPlayerStoppedAtError ) ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : onError ( _audioData [ _audioCurrent ] . audio ) ; break ;
case OverviewFiles : onError ( _songData [ _songCurrent ] . song ) ; break ;
2015-07-03 08:47:16 +00:00
}
return false ;
2015-06-30 21:07:05 +00:00
}
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
data - > started = data - > position = pos + data - > skipStart ;
2014-09-04 07:33:44 +00:00
return true ;
}
2015-07-03 08:47:16 +00:00
bool AudioPlayer : : fadedStop ( MediaOverviewType type , bool * fadedStart ) {
2015-06-30 21:07:05 +00:00
Msg * current = 0 ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : current = & _audioData [ _audioCurrent ] ; break ;
case OverviewFiles : current = & _songData [ _songCurrent ] ; break ;
2015-06-30 21:07:05 +00:00
}
if ( ! current ) return false ;
2014-09-04 07:33:44 +00:00
2015-06-30 21:07:05 +00:00
switch ( current - > state ) {
case AudioPlayerStarting :
case AudioPlayerResuming :
case AudioPlayerPlaying :
current - > state = AudioPlayerFinishing ;
updateCurrentStarted ( type ) ;
2015-07-03 08:47:16 +00:00
if ( fadedStart ) * fadedStart = true ;
2015-06-30 21:07:05 +00:00
break ;
case AudioPlayerPausing :
current - > state = AudioPlayerFinishing ;
2015-07-03 08:47:16 +00:00
if ( fadedStart ) * fadedStart = true ;
2015-06-30 21:07:05 +00:00
break ;
case AudioPlayerPaused :
2015-07-03 08:47:16 +00:00
case AudioPlayerPausedAtEnd :
setStoppedState ( current ) ;
2015-06-30 21:07:05 +00:00
return true ;
}
return false ;
}
2015-07-03 08:47:16 +00:00
void AudioPlayer : : play ( const AudioMsgId & audio , int64 position ) {
2015-06-30 21:07:05 +00:00
AudioMsgId stopped ;
2015-05-30 16:30:47 +00:00
{
QMutexLocker lock ( & playerMutex ) ;
2015-06-30 21:07:05 +00:00
bool fadedStart = false ;
AudioMsg * current = & _audioData [ _audioCurrent ] ;
if ( current - > audio ! = audio ) {
2016-02-12 16:35:06 +00:00
if ( fadedStop ( OverviewVoiceFiles , & fadedStart ) ) {
2015-06-30 21:07:05 +00:00
stopped = current - > audio ;
2015-05-30 16:30:47 +00:00
}
2015-06-30 21:07:05 +00:00
if ( current - > audio ) {
emit loaderOnCancel ( current - > audio ) ;
2015-05-30 16:30:47 +00:00
emit faderOnTimer ( ) ;
}
2014-09-04 07:33:44 +00:00
2015-06-30 21:07:05 +00:00
int32 index = 0 ;
for ( ; index < AudioVoiceMsgSimultaneously ; + + index ) {
if ( _audioData [ index ] . audio = = audio ) {
_audioCurrent = index ;
break ;
}
2015-05-30 16:30:47 +00:00
}
2015-06-30 21:07:05 +00:00
if ( index = = AudioVoiceMsgSimultaneously & & + + _audioCurrent > = AudioVoiceMsgSimultaneously ) {
_audioCurrent - = AudioVoiceMsgSimultaneously ;
}
current = & _audioData [ _audioCurrent ] ;
2015-05-30 16:30:47 +00:00
}
2015-06-30 21:07:05 +00:00
current - > audio = audio ;
2015-11-26 17:34:52 +00:00
current - > file = audio . audio - > location ( true ) ;
2015-12-24 19:26:28 +00:00
current - > data = audio . audio - > data ( ) ;
2015-11-26 17:34:52 +00:00
if ( current - > file . isEmpty ( ) & & current - > data . isEmpty ( ) ) {
2015-07-03 08:47:16 +00:00
setStoppedState ( current , AudioPlayerStoppedAtError ) ;
2015-05-30 16:30:47 +00:00
onError ( audio ) ;
2015-07-03 08:47:16 +00:00
} else {
2015-06-30 21:07:05 +00:00
current - > state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying ;
current - > loading = true ;
2015-07-03 08:47:16 +00:00
emit loaderOnStart ( audio , position ) ;
2015-06-30 21:07:05 +00:00
emit suppressSong ( ) ;
}
}
if ( stopped ) emit updated ( stopped ) ;
}
2015-07-03 08:47:16 +00:00
void AudioPlayer : : play ( const SongMsgId & song , int64 position ) {
2015-06-30 21:07:05 +00:00
SongMsgId stopped ;
{
QMutexLocker lock ( & playerMutex ) ;
bool fadedStart = false ;
SongMsg * current = & _songData [ _songCurrent ] ;
if ( current - > song ! = song ) {
2016-02-12 16:35:06 +00:00
if ( fadedStop ( OverviewFiles , & fadedStart ) ) {
2015-06-30 21:07:05 +00:00
stopped = current - > song ;
}
if ( current - > song ) {
emit loaderOnCancel ( current - > song ) ;
emit faderOnTimer ( ) ;
}
int32 index = 0 ;
for ( ; index < AudioSongSimultaneously ; + + index ) {
if ( _songData [ index ] . song = = song ) {
_songCurrent = index ;
break ;
}
}
if ( index = = AudioSongSimultaneously & & + + _songCurrent > = AudioSongSimultaneously ) {
_songCurrent - = AudioSongSimultaneously ;
}
current = & _songData [ _songCurrent ] ;
}
current - > song = song ;
2015-11-26 17:34:52 +00:00
current - > file = song . song - > location ( true ) ;
2015-12-24 19:26:28 +00:00
current - > data = song . song - > data ( ) ;
2015-11-26 17:34:52 +00:00
if ( current - > file . isEmpty ( ) & & current - > data . isEmpty ( ) ) {
2015-07-03 08:47:16 +00:00
setStoppedState ( current ) ;
2015-12-24 19:26:28 +00:00
if ( ! song . song - > loading ( ) ) {
2016-03-29 17:17:00 +00:00
DocumentOpenClickHandler : : doOpen ( song . song ) ;
2015-07-03 08:47:16 +00:00
}
} else {
2015-06-30 21:07:05 +00:00
current - > state = fadedStart ? AudioPlayerStarting : AudioPlayerPlaying ;
current - > loading = true ;
2015-07-03 08:47:16 +00:00
emit loaderOnStart ( song , position ) ;
2014-09-04 07:33:44 +00:00
}
}
2015-05-30 16:30:47 +00:00
if ( stopped ) emit updated ( stopped ) ;
2014-09-04 07:33:44 +00:00
}
2015-07-03 08:47:16 +00:00
bool AudioPlayer : : checkCurrentALError ( MediaOverviewType type ) {
if ( _checkALError ( ) ) return true ;
2016-01-05 06:59:57 +00:00
2015-07-03 08:47:16 +00:00
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles :
2015-07-03 08:47:16 +00:00
setStoppedState ( & _audioData [ _audioCurrent ] , AudioPlayerStoppedAtError ) ;
onError ( _audioData [ _audioCurrent ] . audio ) ;
break ;
2016-02-12 16:35:06 +00:00
case OverviewFiles :
2015-07-03 08:47:16 +00:00
setStoppedState ( & _songData [ _songCurrent ] , AudioPlayerStoppedAtError ) ;
onError ( _songData [ _songCurrent ] . song ) ;
break ;
}
return false ;
}
void AudioPlayer : : pauseresume ( MediaOverviewType type , bool fast ) {
2015-05-29 18:52:43 +00:00
QMutexLocker lock ( & playerMutex ) ;
2014-09-04 07:33:44 +00:00
2015-06-30 21:07:05 +00:00
Msg * current = 0 ;
float64 suppressGain = 1. ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles :
2015-06-30 21:07:05 +00:00
current = & _audioData [ _audioCurrent ] ;
suppressGain = suppressAllGain ;
break ;
2016-02-12 16:35:06 +00:00
case OverviewFiles :
2015-06-30 21:07:05 +00:00
current = & _songData [ _songCurrent ] ;
2015-07-03 08:47:16 +00:00
suppressGain = suppressSongGain * cSongVolume ( ) ;
2015-06-30 21:07:05 +00:00
break ;
}
switch ( current - > state ) {
2015-05-29 18:52:43 +00:00
case AudioPlayerPausing :
case AudioPlayerPaused :
2015-07-03 08:47:16 +00:00
case AudioPlayerPausedAtEnd : {
2015-06-30 21:07:05 +00:00
if ( current - > state = = AudioPlayerPaused ) {
updateCurrentStarted ( type ) ;
2015-07-03 08:47:16 +00:00
} else if ( current - > state = = AudioPlayerPausedAtEnd ) {
if ( alIsSource ( current - > source ) ) {
alSourcei ( current - > source , AL_SAMPLE_OFFSET , qMax ( current - > position - current - > skipStart , 0LL ) ) ;
if ( ! checkCurrentALError ( type ) ) return ;
}
}
current - > state = fast ? AudioPlayerPlaying : AudioPlayerResuming ;
ALint state = AL_INITIAL ;
alGetSourcei ( current - > source , AL_SOURCE_STATE , & state ) ;
if ( ! checkCurrentALError ( type ) ) return ;
if ( state ! = AL_PLAYING ) {
audioPlayer ( ) - > resumeDevice ( ) ;
alSourcef ( current - > source , AL_GAIN , suppressGain ) ;
if ( ! checkCurrentALError ( type ) ) return ;
alSourcePlay ( current - > source ) ;
if ( ! checkCurrentALError ( type ) ) return ;
2014-09-04 07:33:44 +00:00
}
2016-02-12 16:35:06 +00:00
if ( type = = OverviewVoiceFiles ) emit suppressSong ( ) ;
2015-07-03 08:47:16 +00:00
} break ;
2015-05-29 18:52:43 +00:00
case AudioPlayerStarting :
case AudioPlayerResuming :
case AudioPlayerPlaying :
2015-06-30 21:07:05 +00:00
current - > state = AudioPlayerPausing ;
updateCurrentStarted ( type ) ;
2016-02-12 16:35:06 +00:00
if ( type = = OverviewVoiceFiles ) emit unsuppressSong ( ) ;
2014-09-04 07:33:44 +00:00
break ;
2015-06-30 21:07:05 +00:00
case AudioPlayerFinishing : current - > state = AudioPlayerPausing ; break ;
2014-09-04 07:33:44 +00:00
}
emit faderOnTimer ( ) ;
}
2015-07-03 08:47:16 +00:00
void AudioPlayer : : seek ( int64 position ) {
QMutexLocker lock ( & playerMutex ) ;
2016-02-12 16:35:06 +00:00
MediaOverviewType type = OverviewFiles ;
2015-07-03 08:47:16 +00:00
Msg * current = 0 ;
float64 suppressGain = 1. ;
AudioMsgId audio ;
SongMsgId song ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles :
2015-07-03 08:47:16 +00:00
current = & _audioData [ _audioCurrent ] ;
audio = _audioData [ _audioCurrent ] . audio ;
suppressGain = suppressAllGain ;
break ;
2016-02-12 16:35:06 +00:00
case OverviewFiles :
2015-07-03 08:47:16 +00:00
current = & _songData [ _songCurrent ] ;
song = _songData [ _songCurrent ] . song ;
suppressGain = suppressSongGain * cSongVolume ( ) ;
break ;
}
bool isSource = alIsSource ( current - > source ) ;
bool fastSeek = ( position > = current - > skipStart & & position < current - > duration - current - > skipEnd - ( current - > skipEnd ? AudioVoiceMsgFrequency : 0 ) ) ;
if ( fastSeek & & isSource ) {
alSourcei ( current - > source , AL_SAMPLE_OFFSET , position - current - > skipStart ) ;
if ( ! checkCurrentALError ( type ) ) return ;
alSourcef ( current - > source , AL_GAIN , 1. * suppressGain ) ;
if ( ! checkCurrentALError ( type ) ) return ;
updateCurrentStarted ( type , position - current - > skipStart ) ;
} else {
setStoppedState ( current ) ;
if ( isSource ) alSourceStop ( current - > source ) ;
}
switch ( current - > state ) {
case AudioPlayerPausing :
case AudioPlayerPaused :
case AudioPlayerPausedAtEnd : {
if ( current - > state = = AudioPlayerPausedAtEnd ) {
current - > state = AudioPlayerPaused ;
}
lock . unlock ( ) ;
return pauseresume ( type , true ) ;
} break ;
case AudioPlayerStarting :
case AudioPlayerResuming :
case AudioPlayerPlaying :
current - > state = AudioPlayerPausing ;
updateCurrentStarted ( type ) ;
2016-02-12 16:35:06 +00:00
if ( type = = OverviewVoiceFiles ) emit unsuppressSong ( ) ;
2015-07-03 08:47:16 +00:00
break ;
case AudioPlayerFinishing :
case AudioPlayerStopped :
case AudioPlayerStoppedAtEnd :
case AudioPlayerStoppedAtError :
case AudioPlayerStoppedAtStart :
lock . unlock ( ) ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : if ( audio ) return play ( audio , position ) ;
case OverviewFiles : if ( song ) return play ( song , position ) ;
2015-07-03 08:47:16 +00:00
}
}
emit faderOnTimer ( ) ;
}
void AudioPlayer : : stop ( MediaOverviewType type ) {
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : {
2015-11-24 16:19:18 +00:00
AudioMsgId current ;
{
QMutexLocker lock ( & playerMutex ) ;
current = _audioData [ _audioCurrent ] . audio ;
fadedStop ( type ) ;
}
if ( current ) emit updated ( current ) ;
} break ;
2016-02-12 16:35:06 +00:00
case OverviewFiles : {
2015-11-24 16:19:18 +00:00
SongMsgId current ;
{
QMutexLocker lock ( & playerMutex ) ;
current = _songData [ _songCurrent ] . song ;
fadedStop ( type ) ;
}
if ( current ) emit updated ( current ) ;
} break ;
}
}
void AudioPlayer : : stopAndClear ( ) {
AudioMsg * current_audio = 0 ;
{
QMutexLocker lock ( & playerMutex ) ;
current_audio = & _audioData [ _audioCurrent ] ;
if ( current_audio ) {
setStoppedState ( current_audio ) ;
}
}
SongMsg * current_song = 0 ;
{
QMutexLocker lock ( & playerMutex ) ;
current_song = & _songData [ _songCurrent ] ;
if ( current_song ) {
setStoppedState ( current_song ) ;
}
}
if ( current_song ) {
emit updated ( current_song - > song ) ;
}
if ( current_audio ) {
emit updated ( current_audio - > audio ) ;
}
{
QMutexLocker lock ( & playerMutex ) ;
for ( int32 index = 0 ; index < AudioVoiceMsgSimultaneously ; + + index ) {
if ( _audioData [ index ] . audio ) {
emit loaderOnCancel ( _audioData [ index ] . audio ) ;
}
_audioData [ index ] . clear ( ) ;
if ( _songData [ index ] . song ) {
emit loaderOnCancel ( _songData [ index ] . song ) ;
}
_songData [ index ] . clear ( ) ;
}
2015-07-03 08:47:16 +00:00
}
}
2015-06-30 21:07:05 +00:00
void AudioPlayer : : currentState ( AudioMsgId * audio , AudioPlayerState * state , int64 * position , int64 * duration , int32 * frequency ) {
QMutexLocker lock ( & playerMutex ) ;
AudioMsg * current = & _audioData [ _audioCurrent ] ;
if ( audio ) * audio = current - > audio ;
return currentState ( current , state , position , duration , frequency ) ;
}
void AudioPlayer : : currentState ( SongMsgId * song , AudioPlayerState * state , int64 * position , int64 * duration , int32 * frequency ) {
QMutexLocker lock ( & playerMutex ) ;
SongMsg * current = & _songData [ _songCurrent ] ;
if ( song ) * song = current - > song ;
return currentState ( current , state , position , duration , frequency ) ;
}
void AudioPlayer : : currentState ( Msg * current , AudioPlayerState * state , int64 * position , int64 * duration , int32 * frequency ) {
if ( state ) * state = current - > state ;
if ( position ) * position = current - > position ;
if ( duration ) * duration = current - > duration ;
if ( frequency ) * frequency = current - > frequency ;
}
2015-07-03 08:47:16 +00:00
void AudioPlayer : : setStoppedState ( Msg * current , AudioPlayerState state ) {
current - > state = state ;
current - > position = 0 ;
}
2015-06-30 21:07:05 +00:00
void AudioPlayer : : clearStoppedAtStart ( const AudioMsgId & audio ) {
2015-05-29 18:52:43 +00:00
QMutexLocker lock ( & playerMutex ) ;
2015-06-30 21:07:05 +00:00
if ( _audioData [ _audioCurrent ] . audio = = audio & & _audioData [ _audioCurrent ] . state = = AudioPlayerStoppedAtStart ) {
2015-07-03 08:47:16 +00:00
setStoppedState ( & _audioData [ _audioCurrent ] ) ;
2015-06-30 21:07:05 +00:00
}
2015-05-24 17:58:39 +00:00
}
2015-06-30 21:07:05 +00:00
void AudioPlayer : : clearStoppedAtStart ( const SongMsgId & song ) {
2015-05-29 18:52:43 +00:00
QMutexLocker lock ( & playerMutex ) ;
2015-06-30 21:07:05 +00:00
if ( _songData [ _songCurrent ] . song = = song & & _songData [ _songCurrent ] . state = = AudioPlayerStoppedAtStart ) {
2015-07-03 08:47:16 +00:00
setStoppedState ( & _songData [ _songCurrent ] ) ;
2015-05-24 17:58:39 +00:00
}
2014-09-04 07:33:44 +00:00
}
2015-06-03 12:18:46 +00:00
void AudioPlayer : : resumeDevice ( ) {
_fader - > resumeDevice ( ) ;
2015-01-10 13:08:30 +00:00
}
2015-05-29 18:52:43 +00:00
AudioCapture : : AudioCapture ( ) : _capture ( new AudioCaptureInner ( & _captureThread ) ) {
connect ( this , SIGNAL ( captureOnStart ( ) ) , _capture , SLOT ( onStart ( ) ) ) ;
connect ( this , SIGNAL ( captureOnStop ( bool ) ) , _capture , SLOT ( onStop ( bool ) ) ) ;
2016-02-12 16:35:06 +00:00
connect ( _capture , SIGNAL ( done ( QByteArray , VoiceWaveform , qint32 ) ) , this , SIGNAL ( onDone ( QByteArray , VoiceWaveform , qint32 ) ) ) ;
connect ( _capture , SIGNAL ( update ( quint16 , qint32 ) ) , this , SIGNAL ( onUpdate ( quint16 , qint32 ) ) ) ;
2015-05-29 18:52:43 +00:00
connect ( _capture , SIGNAL ( error ( ) ) , this , SIGNAL ( onError ( ) ) ) ;
connect ( & _captureThread , SIGNAL ( started ( ) ) , _capture , SLOT ( onInit ( ) ) ) ;
connect ( & _captureThread , SIGNAL ( finished ( ) ) , _capture , SLOT ( deleteLater ( ) ) ) ;
_captureThread . start ( ) ;
}
void AudioCapture : : start ( ) {
emit captureOnStart ( ) ;
}
void AudioCapture : : stop ( bool needResult ) {
emit captureOnStop ( needResult ) ;
}
2015-06-01 10:58:46 +00:00
bool AudioCapture : : check ( ) {
if ( const ALCchar * def = alcGetString ( 0 , ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER ) ) {
if ( ALCdevice * dev = alcCaptureOpenDevice ( def , AudioVoiceMsgFrequency , AL_FORMAT_MONO16 , AudioVoiceMsgFrequency / 5 ) ) {
alcCaptureCloseDevice ( dev ) ;
return _checkALCError ( ) ;
}
}
return false ;
}
2015-05-29 18:52:43 +00:00
AudioCapture : : ~ AudioCapture ( ) {
capture = 0 ;
_captureThread . quit ( ) ;
_captureThread . wait ( ) ;
}
AudioPlayer * audioPlayer ( ) {
return player ;
}
AudioCapture * audioCapture ( ) {
return capture ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
AudioPlayerFader : : AudioPlayerFader ( QThread * thread ) : _timer ( this ) , _pauseFlag ( false ) , _paused ( true ) ,
_suppressAll ( false ) , _suppressAllAnim ( false ) , _suppressSong ( false ) , _suppressSongAnim ( false ) ,
_suppressAllGain ( 1. , 1. ) , _suppressSongGain ( 1. , 1. ) ,
_suppressAllStart ( 0 ) , _suppressSongStart ( 0 ) {
2014-09-04 07:33:44 +00:00
moveToThread ( thread ) ;
_timer . moveToThread ( thread ) ;
2015-06-03 12:18:46 +00:00
_pauseTimer . moveToThread ( thread ) ;
2015-01-10 13:08:30 +00:00
2014-09-04 07:33:44 +00:00
_timer . setSingleShot ( true ) ;
connect ( & _timer , SIGNAL ( timeout ( ) ) , this , SLOT ( onTimer ( ) ) ) ;
2015-01-10 13:08:30 +00:00
2015-06-03 12:18:46 +00:00
_pauseTimer . setSingleShot ( true ) ;
connect ( & _pauseTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( onPauseTimer ( ) ) ) ;
connect ( this , SIGNAL ( stopPauseDevice ( ) ) , this , SLOT ( onPauseTimerStop ( ) ) , Qt : : QueuedConnection ) ;
2014-09-04 07:33:44 +00:00
}
2015-05-29 18:52:43 +00:00
void AudioPlayerFader : : onInit ( ) {
2014-09-04 07:33:44 +00:00
}
2015-05-29 18:52:43 +00:00
void AudioPlayerFader : : onTimer ( ) {
QMutexLocker lock ( & playerMutex ) ;
AudioPlayer * voice = audioPlayer ( ) ;
2014-09-04 07:33:44 +00:00
if ( ! voice ) return ;
2015-06-30 21:07:05 +00:00
bool suppressAudioChanged = false , suppressSongChanged = false ;
if ( _suppressAll | | _suppressSongAnim ) {
uint64 ms = getms ( ) ;
float64 wasSong = suppressSongGain ;
if ( _suppressAll ) {
float64 wasAudio = suppressAllGain ;
if ( ms > = _suppressAllStart + notifyLengthMs | | ms < _suppressAllStart ) {
_suppressAll = _suppressAllAnim = false ;
_suppressAllGain = anim : : fvalue ( 1. , 1. ) ;
} else if ( ms > _suppressAllStart + notifyLengthMs - AudioFadeDuration ) {
if ( _suppressAllGain . to ( ) ! = 1. ) _suppressAllGain . start ( 1. ) ;
_suppressAllGain . update ( 1. - ( ( _suppressAllStart + notifyLengthMs - ms ) / float64 ( AudioFadeDuration ) ) , anim : : linear ) ;
} else if ( ms > = _suppressAllStart + st : : notifyFastAnim ) {
if ( _suppressAllAnim ) {
_suppressAllGain . finish ( ) ;
_suppressAllAnim = false ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
} else if ( ms > _suppressAllStart ) {
_suppressAllGain . update ( ( ms - _suppressAllStart ) / st : : notifyFastAnim , anim : : linear ) ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
suppressAllGain = _suppressAllGain . current ( ) ;
suppressAudioChanged = ( suppressAllGain ! = wasAudio ) ;
}
if ( _suppressSongAnim ) {
if ( ms > = _suppressSongStart + AudioFadeDuration ) {
_suppressSongGain . finish ( ) ;
_suppressSongAnim = false ;
} else {
_suppressSongGain . update ( ( ms - _suppressSongStart ) / float64 ( AudioFadeDuration ) , anim : : linear ) ;
2014-09-04 07:33:44 +00:00
}
}
2015-06-30 21:07:05 +00:00
suppressSongGain = qMin ( suppressAllGain , _suppressSongGain . current ( ) ) ;
suppressSongChanged = ( suppressSongGain ! = wasSong ) ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
bool hasFading = ( _suppressAll | | _suppressSongAnim ) , hasPlaying = false ;
for ( int32 i = 0 ; i < AudioVoiceMsgSimultaneously ; + + i ) {
AudioPlayer : : AudioMsg & m ( voice - > _audioData [ i ] ) ;
2015-07-03 08:47:16 +00:00
if ( ( m . state & AudioPlayerStoppedMask ) | | m . state = = AudioPlayerPaused | | ! m . source ) continue ;
2015-06-30 21:07:05 +00:00
int32 emitSignals = updateOnePlayback ( & m , hasPlaying , hasFading , suppressAllGain , suppressAudioChanged ) ;
if ( emitSignals & EmitError ) emit error ( m . audio ) ;
if ( emitSignals & EmitStopped ) emit audioStopped ( m . audio ) ;
if ( emitSignals & EmitPositionUpdated ) emit playPositionUpdated ( m . audio ) ;
if ( emitSignals & EmitNeedToPreload ) emit needToPreload ( m . audio ) ;
}
for ( int32 i = 0 ; i < AudioSongSimultaneously ; + + i ) {
AudioPlayer : : SongMsg & m ( voice - > _songData [ i ] ) ;
2015-07-03 08:47:16 +00:00
if ( ( m . state & AudioPlayerStoppedMask ) | | m . state = = AudioPlayerPaused | | ! m . source ) continue ;
2015-06-30 21:07:05 +00:00
2015-07-03 08:47:16 +00:00
int32 emitSignals = updateOnePlayback ( & m , hasPlaying , hasFading , suppressSongGain * cSongVolume ( ) , suppressSongChanged | | _songVolumeChanged ) ;
2015-06-30 21:07:05 +00:00
if ( emitSignals & EmitError ) emit error ( m . song ) ;
if ( emitSignals & EmitStopped ) emit audioStopped ( m . song ) ;
if ( emitSignals & EmitPositionUpdated ) emit playPositionUpdated ( m . song ) ;
if ( emitSignals & EmitNeedToPreload ) emit needToPreload ( m . song ) ;
}
2015-07-03 08:47:16 +00:00
_songVolumeChanged = false ;
2015-06-30 21:07:05 +00:00
if ( ! hasFading ) {
if ( ! hasPlaying ) {
ALint state = AL_INITIAL ;
alGetSourcei ( notifySource , AL_SOURCE_STATE , & state ) ;
if ( _checkALError ( ) & & state = = AL_PLAYING ) {
hasPlaying = true ;
}
2015-01-10 13:08:30 +00:00
}
}
2014-09-04 07:33:44 +00:00
if ( hasFading ) {
_timer . start ( AudioFadeTimeout ) ;
2015-06-03 12:18:46 +00:00
resumeDevice ( ) ;
2014-09-04 07:33:44 +00:00
} else if ( hasPlaying ) {
_timer . start ( AudioCheckPositionTimeout ) ;
2015-06-03 12:18:46 +00:00
resumeDevice ( ) ;
2015-01-10 13:08:30 +00:00
} else {
2015-06-03 12:18:46 +00:00
QMutexLocker lock ( & _pauseMutex ) ;
_pauseFlag = true ;
_pauseTimer . start ( AudioPauseDeviceTimeout ) ;
2014-09-04 07:33:44 +00:00
}
}
2015-06-30 21:07:05 +00:00
int32 AudioPlayerFader : : updateOnePlayback ( AudioPlayer : : Msg * m , bool & hasPlaying , bool & hasFading , float64 suppressGain , bool suppressGainChanged ) {
bool playing = false , fading = false ;
ALint pos = 0 ;
ALint state = AL_INITIAL ;
alGetSourcei ( m - > source , AL_SAMPLE_OFFSET , & pos ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
2015-06-30 21:07:05 +00:00
alGetSourcei ( m - > source , AL_SOURCE_STATE , & state ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
2015-06-30 21:07:05 +00:00
int32 emitSignals = 0 ;
switch ( m - > state ) {
case AudioPlayerFinishing :
case AudioPlayerPausing :
case AudioPlayerStarting :
case AudioPlayerResuming :
fading = true ;
break ;
case AudioPlayerPlaying :
playing = true ;
break ;
}
if ( fading & & ( state = = AL_PLAYING | | ! m - > loading ) ) {
if ( state ! = AL_PLAYING ) {
fading = false ;
if ( m - > source ) {
alSourceStop ( m - > source ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
alSourcef ( m - > source , AL_GAIN , 1 ) ;
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
}
if ( m - > state = = AudioPlayerPausing ) {
m - > state = AudioPlayerPausedAtEnd ;
} else {
setStoppedState ( m , AudioPlayerStoppedAtEnd ) ;
2015-06-30 21:07:05 +00:00
}
emitSignals | = EmitStopped ;
} else if ( 1000 * ( pos + m - > skipStart - m - > started ) > = AudioFadeDuration * m - > frequency ) {
fading = false ;
alSourcef ( m - > source , AL_GAIN , 1. * suppressGain ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
2015-06-30 21:07:05 +00:00
switch ( m - > state ) {
2015-07-03 08:47:16 +00:00
case AudioPlayerFinishing :
alSourceStop ( m - > source ) ;
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
setStoppedState ( m ) ;
state = AL_STOPPED ;
break ;
case AudioPlayerPausing :
alSourcePause ( m - > source ) ;
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
m - > state = AudioPlayerPaused ;
break ;
2015-06-30 21:07:05 +00:00
case AudioPlayerStarting :
case AudioPlayerResuming :
m - > state = AudioPlayerPlaying ;
playing = true ;
break ;
}
} else {
float64 newGain = 1000. * ( pos + m - > skipStart - m - > started ) / ( AudioFadeDuration * m - > frequency ) ;
if ( m - > state = = AudioPlayerPausing | | m - > state = = AudioPlayerFinishing ) {
newGain = 1. - newGain ;
}
alSourcef ( m - > source , AL_GAIN , newGain * suppressGain ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
2015-06-30 21:07:05 +00:00
}
} else if ( playing & & ( state = = AL_PLAYING | | ! m - > loading ) ) {
if ( state ! = AL_PLAYING ) {
playing = false ;
if ( m - > source ) {
alSourceStop ( m - > source ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
2015-06-30 21:07:05 +00:00
alSourcef ( m - > source , AL_GAIN , 1 ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
2015-06-30 21:07:05 +00:00
}
2015-07-03 08:47:16 +00:00
setStoppedState ( m , AudioPlayerStoppedAtEnd ) ;
2015-06-30 21:07:05 +00:00
emitSignals | = EmitStopped ;
} else if ( suppressGainChanged ) {
alSourcef ( m - > source , AL_GAIN , suppressGain ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) { setStoppedState ( m , AudioPlayerStoppedAtError ) ; return EmitError ; }
2015-06-30 21:07:05 +00:00
}
}
if ( state = = AL_PLAYING & & pos + m - > skipStart - m - > position > = AudioCheckPositionDelta ) {
m - > position = pos + m - > skipStart ;
emitSignals | = EmitPositionUpdated ;
}
if ( playing | | m - > state = = AudioPlayerStarting | | m - > state = = AudioPlayerResuming ) {
if ( ! m - > loading & & m - > skipEnd > 0 & & m - > position + AudioPreloadSamples + m - > skipEnd > m - > duration ) {
m - > loading = true ;
emitSignals | = EmitNeedToPreload ;
}
}
if ( playing ) hasPlaying = true ;
if ( fading ) hasFading = true ;
return emitSignals ;
}
2015-07-03 08:47:16 +00:00
void AudioPlayerFader : : setStoppedState ( AudioPlayer : : Msg * m , AudioPlayerState state ) {
m - > state = state ;
m - > position = 0 ;
}
2015-06-03 12:18:46 +00:00
void AudioPlayerFader : : onPauseTimer ( ) {
QMutexLocker lock ( & _pauseMutex ) ;
if ( _pauseFlag ) {
_paused = true ;
alcDevicePauseSOFT ( audioDevice ) ;
2015-01-10 13:08:30 +00:00
}
}
2015-06-03 12:18:46 +00:00
void AudioPlayerFader : : onPauseTimerStop ( ) {
if ( _pauseTimer . isActive ( ) ) _pauseTimer . stop ( ) ;
2015-01-10 13:08:30 +00:00
}
2015-06-30 21:07:05 +00:00
void AudioPlayerFader : : onSuppressSong ( ) {
if ( ! _suppressSong ) {
_suppressSong = true ;
_suppressSongAnim = true ;
_suppressSongStart = getms ( ) ;
_suppressSongGain . start ( st : : suppressSong ) ;
onTimer ( ) ;
}
}
void AudioPlayerFader : : onUnsuppressSong ( ) {
if ( _suppressSong ) {
_suppressSong = false ;
_suppressSongAnim = true ;
_suppressSongStart = getms ( ) ;
_suppressSongGain . start ( 1. ) ;
onTimer ( ) ;
}
}
void AudioPlayerFader : : onSuppressAll ( ) {
_suppressAll = true ;
_suppressAllStart = getms ( ) ;
_suppressAllGain . start ( st : : suppressAll ) ;
onTimer ( ) ;
}
2015-07-03 08:47:16 +00:00
void AudioPlayerFader : : onSongVolumeChanged ( ) {
_songVolumeChanged = true ;
onTimer ( ) ;
}
2015-06-03 12:18:46 +00:00
void AudioPlayerFader : : resumeDevice ( ) {
QMutexLocker lock ( & _pauseMutex ) ;
_pauseFlag = false ;
emit stopPauseDevice ( ) ;
if ( _paused ) {
_paused = false ;
alcDeviceResumeSOFT ( audioDevice ) ;
2015-06-03 11:57:14 +00:00
}
2015-01-10 13:08:30 +00:00
}
2015-05-29 18:52:43 +00:00
class AudioPlayerLoader {
2015-05-24 17:58:39 +00:00
public :
2015-11-26 17:34:52 +00:00
AudioPlayerLoader ( const FileLocation & file , const QByteArray & data ) : file ( file ) , access ( false ) , data ( data ) , dataPos ( 0 ) {
2015-05-24 17:58:39 +00:00
}
2015-05-29 18:52:43 +00:00
virtual ~ AudioPlayerLoader ( ) {
2015-11-26 17:34:52 +00:00
if ( access ) {
file . accessDisable ( ) ;
access = false ;
}
2015-05-24 17:58:39 +00:00
}
2015-11-26 17:34:52 +00:00
bool check ( const FileLocation & file , const QByteArray & data ) {
return this - > file = = file & & this - > data . size ( ) = = data . size ( ) ;
2015-05-24 17:58:39 +00:00
}
2015-07-03 08:47:16 +00:00
virtual bool open ( qint64 position = 0 ) = 0 ;
2015-05-24 17:58:39 +00:00
virtual int64 duration ( ) = 0 ;
virtual int32 frequency ( ) = 0 ;
virtual int32 format ( ) = 0 ;
2015-07-14 19:07:21 +00:00
virtual int readMore ( QByteArray & result , int64 & samplesAdded ) = 0 ; // < 0 - error, 0 - nothing read, > 0 - read something
2015-05-24 17:58:39 +00:00
protected :
2015-11-26 17:34:52 +00:00
FileLocation file ;
bool access ;
2014-09-04 07:33:44 +00:00
QByteArray data ;
2015-05-24 17:58:39 +00:00
QFile f ;
int32 dataPos ;
2016-01-05 06:59:57 +00:00
2015-05-24 17:58:39 +00:00
bool openFile ( ) {
if ( data . isEmpty ( ) ) {
if ( f . isOpen ( ) ) f . close ( ) ;
2015-11-26 17:34:52 +00:00
if ( ! access ) {
if ( ! file . accessEnable ( ) ) {
LOG ( ( " Audio Error: could not open file access '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( f . error ( ) ) . arg ( f . errorString ( ) ) ) ;
return false ;
}
access = true ;
}
f . setFileName ( file . name ( ) ) ;
2015-05-24 17:58:39 +00:00
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: could not open file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( f . error ( ) ) . arg ( f . errorString ( ) ) ) ;
2015-05-24 17:58:39 +00:00
return false ;
}
}
dataPos = 0 ;
return true ;
}
} ;
2016-02-12 16:35:06 +00:00
class AbstractFFMpegLoader : public AudioPlayerLoader {
2015-05-26 08:19:47 +00:00
public :
2016-02-12 16:35:06 +00:00
AbstractFFMpegLoader ( const FileLocation & file , const QByteArray & data ) : AudioPlayerLoader ( file , data )
, freq ( AudioVoiceMsgFrequency )
, len ( 0 )
, ioBuffer ( 0 )
, ioContext ( 0 )
, fmtContext ( 0 )
, codec ( 0 )
, streamId ( 0 )
, _opened ( false ) {
2015-05-26 08:19:47 +00:00
}
2015-07-03 08:47:16 +00:00
bool open ( qint64 position = 0 ) {
2015-05-29 18:52:43 +00:00
if ( ! AudioPlayerLoader : : openFile ( ) ) {
2015-05-26 08:19:47 +00:00
return false ;
}
2016-02-12 16:35:06 +00:00
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2015-05-26 08:19:47 +00:00
ioBuffer = ( uchar * ) av_malloc ( AVBlockSize ) ;
if ( data . isEmpty ( ) ) {
2016-02-12 16:35:06 +00:00
ioContext = avio_alloc_context ( ioBuffer , AVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_file , 0 , & AbstractFFMpegLoader : : _seek_file ) ;
2015-05-26 08:19:47 +00:00
} else {
2016-02-12 16:35:06 +00:00
ioContext = avio_alloc_context ( ioBuffer , AVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_data , 0 , & AbstractFFMpegLoader : : _seek_data ) ;
2015-05-26 08:19:47 +00:00
}
fmtContext = avformat_alloc_context ( ) ;
if ( ! fmtContext ) {
2016-02-12 16:35:06 +00:00
DEBUG_LOG ( ( " Audio Read Error: Unable to avformat_alloc_context for file '%1', data size '%2' " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) ) ;
2015-05-26 08:19:47 +00:00
return false ;
}
2015-05-29 18:52:43 +00:00
fmtContext - > pb = ioContext ;
2015-05-26 08:19:47 +00:00
if ( ( res = avformat_open_input ( & fmtContext , 0 , 0 , 0 ) ) < 0 ) {
2015-07-17 19:17:37 +00:00
ioBuffer = 0 ;
2016-02-12 16:35:06 +00:00
DEBUG_LOG ( ( " Audio Read Error: Unable to avformat_open_input for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-05-26 08:19:47 +00:00
return false ;
}
_opened = true ;
if ( ( res = avformat_find_stream_info ( fmtContext , 0 ) ) < 0 ) {
2016-02-12 16:35:06 +00:00
DEBUG_LOG ( ( " Audio Read Error: Unable to avformat_find_stream_info for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-05-26 08:19:47 +00:00
return false ;
}
streamId = av_find_best_stream ( fmtContext , AVMEDIA_TYPE_AUDIO , - 1 , - 1 , & codec , 0 ) ;
if ( streamId < 0 ) {
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to av_find_best_stream for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( streamId ) . arg ( av_make_error_string ( err , sizeof ( err ) , streamId ) ) ) ;
2015-05-26 08:19:47 +00:00
return false ;
}
2016-02-12 16:35:06 +00:00
freq = fmtContext - > streams [ streamId ] - > codec - > sample_rate ;
2015-08-14 15:47:56 +00:00
if ( fmtContext - > streams [ streamId ] - > duration = = AV_NOPTS_VALUE ) {
len = ( fmtContext - > duration * freq ) / AV_TIME_BASE ;
} else {
len = ( fmtContext - > streams [ streamId ] - > duration * freq * fmtContext - > streams [ streamId ] - > time_base . num ) / fmtContext - > streams [ streamId ] - > time_base . den ;
}
2016-02-12 16:35:06 +00:00
return true ;
}
int64 duration ( ) {
return len ;
}
int32 frequency ( ) {
return freq ;
}
~ AbstractFFMpegLoader ( ) {
if ( ioContext ) av_free ( ioContext ) ;
if ( _opened ) {
avformat_close_input ( & fmtContext ) ;
} else if ( ioBuffer ) {
av_free ( ioBuffer ) ;
}
if ( fmtContext ) avformat_free_context ( fmtContext ) ;
}
protected :
int32 freq ;
int64 len ;
uchar * ioBuffer ;
AVIOContext * ioContext ;
AVFormatContext * fmtContext ;
AVCodec * codec ;
int32 streamId ;
bool _opened ;
private :
static int _read_data ( void * opaque , uint8_t * buf , int buf_size ) {
AbstractFFMpegLoader * l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
int32 nbytes = qMin ( l - > data . size ( ) - l - > dataPos , int32 ( buf_size ) ) ;
if ( nbytes < = 0 ) {
return 0 ;
}
memcpy ( buf , l - > data . constData ( ) + l - > dataPos , nbytes ) ;
l - > dataPos + = nbytes ;
return nbytes ;
}
static int64_t _seek_data ( void * opaque , int64_t offset , int whence ) {
AbstractFFMpegLoader * l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
int32 newPos = - 1 ;
switch ( whence ) {
case SEEK_SET : newPos = offset ; break ;
case SEEK_CUR : newPos = l - > dataPos + offset ; break ;
case SEEK_END : newPos = l - > data . size ( ) + offset ; break ;
}
if ( newPos < 0 | | newPos > l - > data . size ( ) ) {
return - 1 ;
}
l - > dataPos = newPos ;
return l - > dataPos ;
}
static int _read_file ( void * opaque , uint8_t * buf , int buf_size ) {
AbstractFFMpegLoader * l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
return int ( l - > f . read ( ( char * ) ( buf ) , buf_size ) ) ;
}
static int64_t _seek_file ( void * opaque , int64_t offset , int whence ) {
AbstractFFMpegLoader * l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
switch ( whence ) {
case SEEK_SET : return l - > f . seek ( offset ) ? l - > f . pos ( ) : - 1 ;
case SEEK_CUR : return l - > f . seek ( l - > f . pos ( ) + offset ) ? l - > f . pos ( ) : - 1 ;
case SEEK_END : return l - > f . seek ( l - > f . size ( ) + offset ) ? l - > f . pos ( ) : - 1 ;
}
return - 1 ;
}
} ;
static const AVSampleFormat _toFormat = AV_SAMPLE_FMT_S16 ;
static const int64_t _toChannelLayout = AV_CH_LAYOUT_STEREO ;
static const int32 _toChannels = 2 ;
class FFMpegLoader : public AbstractFFMpegLoader {
public :
FFMpegLoader ( const FileLocation & file , const QByteArray & data ) : AbstractFFMpegLoader ( file , data )
, sampleSize ( 2 * sizeof ( uint16 ) )
, fmt ( AL_FORMAT_STEREO16 )
, srcRate ( AudioVoiceMsgFrequency )
, dstRate ( AudioVoiceMsgFrequency )
, maxResampleSamples ( 1024 )
, dstSamplesData ( 0 )
, codecContext ( 0 )
, frame ( 0 )
, swrContext ( 0 ) {
frame = av_frame_alloc ( ) ;
}
bool open ( qint64 position = 0 ) {
if ( ! AbstractFFMpegLoader : : open ( position ) ) {
return false ;
}
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
// Get a pointer to the codec context for the audio stream
av_opt_set_int ( fmtContext - > streams [ streamId ] - > codec , " refcounted_frames " , 1 , 0 ) ;
if ( ( res = avcodec_open2 ( fmtContext - > streams [ streamId ] - > codec , codec , 0 ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to avcodec_open2 for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
return false ;
}
codecContext = fmtContext - > streams [ streamId ] - > codec ;
2015-12-17 17:31:28 +00:00
uint64_t layout = codecContext - > channel_layout ;
inputFormat = codecContext - > sample_fmt ;
2015-05-26 08:19:47 +00:00
switch ( layout ) {
case AV_CH_LAYOUT_MONO :
switch ( inputFormat ) {
case AV_SAMPLE_FMT_U8 :
case AV_SAMPLE_FMT_U8P : fmt = AL_FORMAT_MONO8 ; sampleSize = 1 ; break ;
case AV_SAMPLE_FMT_S16 :
2016-02-12 16:35:06 +00:00
case AV_SAMPLE_FMT_S16P : fmt = AL_FORMAT_MONO16 ; sampleSize = sizeof ( uint16 ) ; break ;
2015-05-26 08:19:47 +00:00
default :
sampleSize = - 1 ; // convert needed
break ;
}
break ;
case AV_CH_LAYOUT_STEREO :
switch ( inputFormat ) {
2016-02-12 16:35:06 +00:00
case AV_SAMPLE_FMT_U8 : fmt = AL_FORMAT_STEREO8 ; sampleSize = 2 ; break ;
case AV_SAMPLE_FMT_S16 : fmt = AL_FORMAT_STEREO16 ; sampleSize = 2 * sizeof ( uint16 ) ; break ;
2015-05-26 08:19:47 +00:00
default :
sampleSize = - 1 ; // convert needed
break ;
}
break ;
default :
sampleSize = - 1 ; // convert needed
break ;
}
if ( freq ! = 44100 & & freq ! = 48000 ) {
sampleSize = - 1 ; // convert needed
}
if ( sampleSize < 0 ) {
swrContext = swr_alloc ( ) ;
if ( ! swrContext ) {
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to swr_alloc for file '%1', data size '%2' " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) ) ;
2015-05-26 08:19:47 +00:00
return false ;
}
int64_t src_ch_layout = layout , dst_ch_layout = _toChannelLayout ;
srcRate = freq ;
AVSampleFormat src_sample_fmt = inputFormat , dst_sample_fmt = _toFormat ;
dstRate = ( freq ! = 44100 & & freq ! = 48000 ) ? AudioVoiceMsgFrequency : freq ;
av_opt_set_int ( swrContext , " in_channel_layout " , src_ch_layout , 0 ) ;
av_opt_set_int ( swrContext , " in_sample_rate " , srcRate , 0 ) ;
av_opt_set_sample_fmt ( swrContext , " in_sample_fmt " , src_sample_fmt , 0 ) ;
av_opt_set_int ( swrContext , " out_channel_layout " , dst_ch_layout , 0 ) ;
av_opt_set_int ( swrContext , " out_sample_rate " , dstRate , 0 ) ;
av_opt_set_sample_fmt ( swrContext , " out_sample_fmt " , dst_sample_fmt , 0 ) ;
if ( ( res = swr_init ( swrContext ) ) < 0 ) {
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to swr_init for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-05-26 08:19:47 +00:00
return false ;
}
sampleSize = _toChannels * sizeof ( short ) ;
freq = dstRate ;
len = av_rescale_rnd ( len , dstRate , srcRate , AV_ROUND_UP ) ;
fmt = AL_FORMAT_STEREO16 ;
maxResampleSamples = av_rescale_rnd ( AVBlockSize / sampleSize , dstRate , srcRate , AV_ROUND_UP ) ;
2015-05-29 18:52:43 +00:00
if ( ( res = av_samples_alloc_array_and_samples ( & dstSamplesData , 0 , _toChannels , maxResampleSamples , _toFormat , 0 ) ) < 0 ) {
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-05-26 08:19:47 +00:00
return false ;
}
}
2015-07-03 08:47:16 +00:00
if ( position ) {
int64 ts = ( position * fmtContext - > streams [ streamId ] - > time_base . den ) / ( freq * fmtContext - > streams [ streamId ] - > time_base . num ) ;
if ( av_seek_frame ( fmtContext , streamId , ts , AVSEEK_FLAG_ANY ) < 0 ) {
if ( av_seek_frame ( fmtContext , streamId , ts , 0 ) < 0 ) {
}
}
//if (dstSamplesData) {
// position = qRound(srcRate * (position / float64(dstRate)));
//}
}
2015-05-26 08:19:47 +00:00
return true ;
}
int32 format ( ) {
return fmt ;
}
2015-07-14 19:07:21 +00:00
int readMore ( QByteArray & result , int64 & samplesAdded ) {
2015-05-26 08:19:47 +00:00
int res ;
if ( ( res = av_read_frame ( fmtContext , & avpkt ) ) < 0 ) {
if ( res ! = AVERROR_EOF ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to av_read_frame() file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-05-26 08:19:47 +00:00
}
2015-07-14 19:07:21 +00:00
return - 1 ;
2015-05-26 08:19:47 +00:00
}
if ( avpkt . stream_index = = streamId ) {
2015-05-30 16:30:47 +00:00
av_frame_unref ( frame ) ;
2015-05-26 08:19:47 +00:00
int got_frame = 0 ;
if ( ( res = avcodec_decode_audio4 ( codecContext , frame , & got_frame , & avpkt ) ) < 0 ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to avcodec_decode_audio4() file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-07-14 19:07:21 +00:00
2015-12-25 13:10:13 +00:00
av_packet_unref ( & avpkt ) ;
2015-07-14 19:07:21 +00:00
if ( res = = AVERROR_INVALIDDATA ) return 0 ; // try to skip bad packet
return - 1 ;
2015-05-26 08:19:47 +00:00
}
if ( got_frame ) {
2015-05-29 18:52:43 +00:00
if ( dstSamplesData ) { // convert needed
2015-05-26 08:19:47 +00:00
int64_t dstSamples = av_rescale_rnd ( swr_get_delay ( swrContext , srcRate ) + frame - > nb_samples , dstRate , srcRate , AV_ROUND_UP ) ;
if ( dstSamples > maxResampleSamples ) {
maxResampleSamples = dstSamples ;
2015-05-29 18:52:43 +00:00
av_free ( dstSamplesData [ 0 ] ) ;
2015-05-26 08:19:47 +00:00
2015-05-29 18:52:43 +00:00
if ( ( res = av_samples_alloc ( dstSamplesData , 0 , _toChannels , maxResampleSamples , _toFormat , 1 ) ) < 0 ) {
dstSamplesData [ 0 ] = 0 ;
2015-05-26 08:19:47 +00:00
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to av_samples_alloc for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-07-14 19:07:21 +00:00
2015-12-25 13:10:13 +00:00
av_packet_unref ( & avpkt ) ;
2015-07-14 19:07:21 +00:00
return - 1 ;
2015-05-26 08:19:47 +00:00
}
}
2015-05-29 18:52:43 +00:00
if ( ( res = swr_convert ( swrContext , dstSamplesData , dstSamples , ( const uint8_t * * ) frame - > extended_data , frame - > nb_samples ) ) < 0 ) {
2015-05-26 08:19:47 +00:00
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2015-11-26 17:34:52 +00:00
LOG ( ( " Audio Error: Unable to swr_convert for file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2015-07-14 19:07:21 +00:00
2015-12-25 13:10:13 +00:00
av_packet_unref ( & avpkt ) ;
2015-07-14 19:07:21 +00:00
return - 1 ;
2015-05-26 08:19:47 +00:00
}
int32 resultLen = av_samples_get_buffer_size ( 0 , _toChannels , res , _toFormat , 1 ) ;
2015-05-29 18:52:43 +00:00
result . append ( ( const char * ) dstSamplesData [ 0 ] , resultLen ) ;
2015-05-26 08:19:47 +00:00
samplesAdded + = resultLen / sampleSize ;
} else {
result . append ( ( const char * ) frame - > extended_data [ 0 ] , frame - > nb_samples * sampleSize ) ;
samplesAdded + = frame - > nb_samples ;
}
}
}
2015-12-25 13:10:13 +00:00
av_packet_unref ( & avpkt ) ;
2015-07-14 19:07:21 +00:00
return 1 ;
2015-05-26 08:19:47 +00:00
}
~ FFMpegLoader ( ) {
if ( codecContext ) avcodec_close ( codecContext ) ;
if ( swrContext ) swr_free ( & swrContext ) ;
2015-05-29 18:52:43 +00:00
if ( dstSamplesData ) {
if ( dstSamplesData [ 0 ] ) {
av_freep ( & dstSamplesData [ 0 ] ) ;
2015-05-26 08:19:47 +00:00
}
2015-05-29 18:52:43 +00:00
av_freep ( & dstSamplesData ) ;
2015-05-26 08:19:47 +00:00
}
av_frame_free ( & frame ) ;
}
2016-02-12 16:35:06 +00:00
protected :
int32 sampleSize ;
2015-05-26 08:19:47 +00:00
private :
2016-02-12 16:35:06 +00:00
int32 fmt ;
int32 srcRate , dstRate , maxResampleSamples ;
2015-05-29 18:52:43 +00:00
uint8_t * * dstSamplesData ;
2015-05-26 08:19:47 +00:00
AVCodecContext * codecContext ;
AVPacket avpkt ;
AVSampleFormat inputFormat ;
AVFrame * frame ;
SwrContext * swrContext ;
} ;
2015-06-30 21:07:05 +00:00
AudioPlayerLoaders : : AudioPlayerLoaders ( QThread * thread ) : _audioLoader ( 0 ) , _songLoader ( 0 ) {
2014-09-04 07:33:44 +00:00
moveToThread ( thread ) ;
}
2015-05-29 18:52:43 +00:00
AudioPlayerLoaders : : ~ AudioPlayerLoaders ( ) {
2015-06-30 21:07:05 +00:00
delete _audioLoader ;
delete _songLoader ;
2014-09-04 07:33:44 +00:00
}
2015-05-29 18:52:43 +00:00
void AudioPlayerLoaders : : onInit ( ) {
2014-09-04 07:33:44 +00:00
}
2015-07-03 08:47:16 +00:00
void AudioPlayerLoaders : : onStart ( const AudioMsgId & audio , qint64 position ) {
2015-06-30 21:07:05 +00:00
_audio = AudioMsgId ( ) ;
delete _audioLoader ;
_audioLoader = 0 ;
2015-07-13 17:56:44 +00:00
{
QMutexLocker lock ( & playerMutex ) ;
AudioPlayer * voice = audioPlayer ( ) ;
if ( ! voice ) return ;
voice - > _audioData [ voice - > _audioCurrent ] . loading = true ;
}
2016-02-12 16:35:06 +00:00
loadData ( OverviewVoiceFiles , static_cast < const void * > ( & audio ) , position ) ;
2014-09-04 07:33:44 +00:00
}
2015-07-03 08:47:16 +00:00
void AudioPlayerLoaders : : onStart ( const SongMsgId & song , qint64 position ) {
2015-06-30 21:07:05 +00:00
_song = SongMsgId ( ) ;
delete _songLoader ;
_songLoader = 0 ;
2015-07-13 17:56:44 +00:00
{
QMutexLocker lock ( & playerMutex ) ;
AudioPlayer * voice = audioPlayer ( ) ;
if ( ! voice ) return ;
voice - > _songData [ voice - > _songCurrent ] . loading = true ;
}
2016-02-12 16:35:06 +00:00
loadData ( OverviewFiles , static_cast < const void * > ( & song ) , position ) ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
void AudioPlayerLoaders : : clear ( MediaOverviewType type ) {
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : clearAudio ( ) ; break ;
case OverviewFiles : clearSong ( ) ; break ;
2015-06-30 21:07:05 +00:00
}
}
2015-05-29 18:52:43 +00:00
2015-07-03 08:47:16 +00:00
void AudioPlayerLoaders : : setStoppedState ( AudioPlayer : : Msg * m , AudioPlayerState state ) {
m - > state = state ;
m - > position = 0 ;
}
2015-06-30 21:07:05 +00:00
void AudioPlayerLoaders : : emitError ( MediaOverviewType type ) {
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : emit error ( clearAudio ( ) ) ; break ;
case OverviewFiles : emit error ( clearSong ( ) ) ; break ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
}
AudioMsgId AudioPlayerLoaders : : clearAudio ( ) {
AudioMsgId current = _audio ;
_audio = AudioMsgId ( ) ;
delete _audioLoader ;
_audioLoader = 0 ;
return current ;
}
SongMsgId AudioPlayerLoaders : : clearSong ( ) {
SongMsgId current = _song ;
_song = SongMsgId ( ) ;
delete _songLoader ;
_songLoader = 0 ;
return current ;
}
void AudioPlayerLoaders : : onLoad ( const AudioMsgId & audio ) {
2016-02-12 16:35:06 +00:00
loadData ( OverviewVoiceFiles , static_cast < const void * > ( & audio ) , 0 ) ;
2015-06-30 21:07:05 +00:00
}
2014-09-04 07:33:44 +00:00
2015-06-30 21:07:05 +00:00
void AudioPlayerLoaders : : onLoad ( const SongMsgId & song ) {
2016-02-12 16:35:06 +00:00
loadData ( OverviewFiles , static_cast < const void * > ( & song ) , 0 ) ;
2015-06-30 21:07:05 +00:00
}
2015-07-03 08:47:16 +00:00
void AudioPlayerLoaders : : loadData ( MediaOverviewType type , const void * objId , qint64 position ) {
2015-06-30 21:07:05 +00:00
SetupError err = SetupNoErrorStarted ;
2015-07-03 08:47:16 +00:00
AudioPlayerLoader * l = setupLoader ( type , objId , err , position ) ;
2015-06-30 21:07:05 +00:00
if ( ! l ) {
if ( err = = SetupErrorAtStart ) {
emitError ( type ) ;
}
2014-09-04 07:33:44 +00:00
return ;
}
2015-07-13 17:56:44 +00:00
bool started = ( err = = SetupNoErrorStarted ) , finished = false , errAtStart = started ;
2014-09-04 07:33:44 +00:00
QByteArray result ;
2015-05-24 17:58:39 +00:00
int64 samplesAdded = 0 , frequency = l - > frequency ( ) , format = l - > format ( ) ;
2014-09-04 07:33:44 +00:00
while ( result . size ( ) < AudioVoiceMsgBufferSize ) {
2015-07-14 19:07:21 +00:00
int res = l - > readMore ( result , samplesAdded ) ;
if ( res < 0 ) {
2015-07-13 17:56:44 +00:00
if ( errAtStart ) {
{
QMutexLocker lock ( & playerMutex ) ;
AudioPlayer : : Msg * m = checkLoader ( type ) ;
if ( m ) m - > state = AudioPlayerStoppedAtStart ;
}
emitError ( type ) ;
return ;
}
2014-09-04 07:33:44 +00:00
finished = true ;
break ;
}
2015-07-14 19:07:21 +00:00
if ( res > 0 ) errAtStart = false ;
2014-09-04 07:33:44 +00:00
2015-06-30 21:07:05 +00:00
QMutexLocker lock ( & playerMutex ) ;
if ( ! checkLoader ( type ) ) {
clear ( type ) ;
return ;
2014-09-04 07:33:44 +00:00
}
}
2015-05-29 18:52:43 +00:00
QMutexLocker lock ( & playerMutex ) ;
2015-06-30 21:07:05 +00:00
AudioPlayer : : Msg * m = checkLoader ( type ) ;
if ( ! m ) {
clear ( type ) ;
return ;
2014-09-04 07:33:44 +00:00
}
if ( started ) {
2015-06-30 21:07:05 +00:00
if ( m - > source ) {
alSourceStop ( m - > source ) ;
2014-09-04 07:33:44 +00:00
for ( int32 i = 0 ; i < 3 ; + + i ) {
2015-06-30 21:07:05 +00:00
if ( m - > samplesCount [ i ] ) {
alSourceUnqueueBuffers ( m - > source , 1 , m - > buffers + i ) ;
m - > samplesCount [ i ] = 0 ;
2014-09-04 07:33:44 +00:00
}
}
2015-06-30 21:07:05 +00:00
m - > nextBuffer = 0 ;
2014-09-04 07:33:44 +00:00
}
2015-07-03 08:47:16 +00:00
m - > skipStart = position ;
m - > skipEnd = m - > duration - position ;
m - > position = 0 ;
m - > started = 0 ;
2014-09-04 07:33:44 +00:00
}
if ( samplesAdded ) {
2015-06-30 21:07:05 +00:00
if ( ! m - > source ) {
alGenSources ( 1 , & m - > source ) ;
alSourcef ( m - > source , AL_PITCH , 1.f ) ;
alSource3f ( m - > source , AL_POSITION , 0 , 0 , 0 ) ;
alSource3f ( m - > source , AL_VELOCITY , 0 , 0 , 0 ) ;
alSourcei ( m - > source , AL_LOOPING , 0 ) ;
}
if ( ! m - > buffers [ m - > nextBuffer ] ) alGenBuffers ( 3 , m - > buffers ) ;
2014-09-04 07:33:44 +00:00
if ( ! _checkALError ( ) ) {
2015-07-03 08:47:16 +00:00
setStoppedState ( m , AudioPlayerStoppedAtError ) ;
2015-06-30 21:07:05 +00:00
emitError ( type ) ;
return ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
if ( m - > samplesCount [ m - > nextBuffer ] ) {
alSourceUnqueueBuffers ( m - > source , 1 , m - > buffers + m - > nextBuffer ) ;
m - > skipStart + = m - > samplesCount [ m - > nextBuffer ] ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
m - > samplesCount [ m - > nextBuffer ] = samplesAdded ;
alBufferData ( m - > buffers [ m - > nextBuffer ] , format , result . constData ( ) , result . size ( ) , frequency ) ;
alSourceQueueBuffers ( m - > source , 1 , m - > buffers + m - > nextBuffer ) ;
m - > skipEnd - = samplesAdded ;
2014-09-04 07:33:44 +00:00
2015-06-30 21:07:05 +00:00
m - > nextBuffer = ( m - > nextBuffer + 1 ) % 3 ;
2014-09-04 07:33:44 +00:00
if ( ! _checkALError ( ) ) {
2015-07-03 08:47:16 +00:00
setStoppedState ( m , AudioPlayerStoppedAtError ) ;
2015-06-30 21:07:05 +00:00
emitError ( type ) ;
return ;
2014-09-04 07:33:44 +00:00
}
} else {
finished = true ;
}
if ( finished ) {
2015-06-30 21:07:05 +00:00
m - > skipEnd = 0 ;
m - > duration = m - > skipStart + m - > samplesCount [ 0 ] + m - > samplesCount [ 1 ] + m - > samplesCount [ 2 ] ;
clear ( type ) ;
2014-09-04 07:33:44 +00:00
}
2015-06-30 21:07:05 +00:00
m - > loading = false ;
if ( m - > state = = AudioPlayerResuming | | m - > state = = AudioPlayerPlaying | | m - > state = = AudioPlayerStarting ) {
2014-09-04 07:33:44 +00:00
ALint state = AL_INITIAL ;
2015-06-30 21:07:05 +00:00
alGetSourcei ( m - > source , AL_SOURCE_STATE , & state ) ;
2014-09-04 07:33:44 +00:00
if ( _checkALError ( ) ) {
if ( state ! = AL_PLAYING ) {
2015-06-30 21:07:05 +00:00
audioPlayer ( ) - > resumeDevice ( ) ;
2015-07-03 08:47:16 +00:00
2015-06-30 21:07:05 +00:00
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : alSourcef ( m - > source , AL_GAIN , suppressAllGain ) ; break ;
case OverviewFiles : alSourcef ( m - > source , AL_GAIN , suppressSongGain * cSongVolume ( ) ) ; break ;
2015-07-03 08:47:16 +00:00
}
if ( ! _checkALError ( ) ) {
setStoppedState ( m , AudioPlayerStoppedAtError ) ;
emitError ( type ) ;
return ;
2015-06-30 21:07:05 +00:00
}
2015-07-03 08:47:16 +00:00
2015-06-30 21:07:05 +00:00
alSourcePlay ( m - > source ) ;
2015-07-03 08:47:16 +00:00
if ( ! _checkALError ( ) ) {
setStoppedState ( m , AudioPlayerStoppedAtError ) ;
emitError ( type ) ;
return ;
}
2014-09-04 07:33:44 +00:00
emit needToCheck ( ) ;
}
2015-07-03 08:47:16 +00:00
} else {
setStoppedState ( m , AudioPlayerStoppedAtError ) ;
emitError ( type ) ;
2014-09-04 07:33:44 +00:00
}
}
}
2015-07-03 08:47:16 +00:00
AudioPlayerLoader * AudioPlayerLoaders : : setupLoader ( MediaOverviewType type , const void * objId , SetupError & err , qint64 position ) {
2015-06-30 21:07:05 +00:00
err = SetupErrorAtStart ;
QMutexLocker lock ( & playerMutex ) ;
AudioPlayer * voice = audioPlayer ( ) ;
2016-03-20 08:16:35 +00:00
if ( ! voice ) return nullptr ;
2015-06-30 21:07:05 +00:00
bool isGoodId = false ;
AudioPlayer : : Msg * m = 0 ;
AudioPlayerLoader * * l = 0 ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : {
2015-06-30 21:07:05 +00:00
AudioPlayer : : AudioMsg & msg ( voice - > _audioData [ voice - > _audioCurrent ] ) ;
const AudioMsgId & audio ( * static_cast < const AudioMsgId * > ( objId ) ) ;
if ( msg . audio ! = audio | | ! msg . loading ) {
emit error ( audio ) ;
break ;
}
m = & msg ;
l = & _audioLoader ;
isGoodId = ( _audio = = audio ) ;
} break ;
2016-02-12 16:35:06 +00:00
case OverviewFiles : {
2015-06-30 21:07:05 +00:00
AudioPlayer : : SongMsg & msg ( voice - > _songData [ voice - > _songCurrent ] ) ;
const SongMsgId & song ( * static_cast < const SongMsgId * > ( objId ) ) ;
if ( msg . song ! = song | | ! msg . loading ) {
emit error ( song ) ;
break ;
}
m = & msg ;
l = & _songLoader ;
isGoodId = ( _song = = song ) ;
} break ;
}
if ( ! l | | ! m ) {
LOG ( ( " Audio Error: trying to load part of audio, that is not current at the moment " ) ) ;
err = SetupErrorNotPlaying ;
2016-03-20 08:16:35 +00:00
return nullptr ;
2015-06-30 21:07:05 +00:00
}
2015-11-26 17:34:52 +00:00
if ( * l & & ( ! isGoodId | | ! ( * l ) - > check ( m - > file , m - > data ) ) ) {
2015-06-30 21:07:05 +00:00
delete * l ;
* l = 0 ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : _audio = AudioMsgId ( ) ; break ;
case OverviewFiles : _song = SongMsgId ( ) ; break ;
2015-06-30 21:07:05 +00:00
}
}
if ( ! * l ) {
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : _audio = * static_cast < const AudioMsgId * > ( objId ) ; break ;
case OverviewFiles : _song = * static_cast < const SongMsgId * > ( objId ) ; break ;
2015-06-30 21:07:05 +00:00
}
2015-11-26 17:34:52 +00:00
// QByteArray header = m->data.mid(0, 8);
// if (header.isEmpty()) {
// QFile f(m->fname);
// if (!f.open(QIODevice::ReadOnly)) {
// LOG(("Audio Error: could not open file '%1'").arg(m->fname));
// m->state = AudioPlayerStoppedAtStart;
2016-03-20 08:16:35 +00:00
// return nullptr;
2015-11-26 17:34:52 +00:00
// }
// header = f.read(8);
// }
// if (header.size() < 8) {
// LOG(("Audio Error: could not read header from file '%1', data size %2").arg(m->fname).arg(m->data.isEmpty() ? QFileInfo(m->fname).size() : m->data.size()));
// m->state = AudioPlayerStoppedAtStart;
2016-03-20 08:16:35 +00:00
// return nullptr;
2015-11-26 17:34:52 +00:00
// }
* l = new FFMpegLoader ( m - > file , m - > data ) ;
2015-06-30 21:07:05 +00:00
2015-07-03 08:47:16 +00:00
if ( ! ( * l ) - > open ( position ) ) {
2015-06-30 21:07:05 +00:00
m - > state = AudioPlayerStoppedAtStart ;
2016-03-20 08:16:35 +00:00
return nullptr ;
2015-06-30 21:07:05 +00:00
}
int64 duration = ( * l ) - > duration ( ) ;
if ( duration < = 0 ) {
m - > state = AudioPlayerStoppedAtStart ;
2016-03-20 08:16:35 +00:00
return nullptr ;
2015-06-30 21:07:05 +00:00
}
m - > duration = duration ;
m - > frequency = ( * l ) - > frequency ( ) ;
if ( ! m - > frequency ) m - > frequency = AudioVoiceMsgFrequency ;
err = SetupNoErrorStarted ;
} else {
if ( ! m - > skipEnd ) {
err = SetupErrorLoadedFull ;
LOG ( ( " Audio Error: trying to load part of audio, that is already loaded to the end " ) ) ;
2016-03-20 08:16:35 +00:00
return nullptr ;
2015-06-30 21:07:05 +00:00
}
}
return * l ;
}
AudioPlayer : : Msg * AudioPlayerLoaders : : checkLoader ( MediaOverviewType type ) {
AudioPlayer * voice = audioPlayer ( ) ;
if ( ! voice ) return 0 ;
bool isGoodId = false ;
AudioPlayer : : Msg * m = 0 ;
AudioPlayerLoader * * l = 0 ;
switch ( type ) {
2016-02-12 16:35:06 +00:00
case OverviewVoiceFiles : {
2015-06-30 21:07:05 +00:00
AudioPlayer : : AudioMsg & msg ( voice - > _audioData [ voice - > _audioCurrent ] ) ;
isGoodId = ( msg . audio = = _audio ) ;
l = & _audioLoader ;
m = & msg ;
} break ;
2016-02-12 16:35:06 +00:00
case OverviewFiles : {
2015-06-30 21:07:05 +00:00
AudioPlayer : : SongMsg & msg ( voice - > _songData [ voice - > _songCurrent ] ) ;
isGoodId = ( msg . song = = _song ) ;
l = & _songLoader ;
m = & msg ;
} break ;
}
if ( ! l | | ! m ) return 0 ;
2015-11-26 17:34:52 +00:00
if ( ! isGoodId | | ! m - > loading | | ! ( * l ) - > check ( m - > file , m - > data ) ) {
2015-06-30 21:07:05 +00:00
LOG ( ( " Audio Error: playing changed while loading " ) ) ;
return 0 ;
}
return m ;
}
void AudioPlayerLoaders : : onCancel ( const AudioMsgId & audio ) {
if ( _audio = = audio ) {
_audio = AudioMsgId ( ) ;
delete _audioLoader ;
_audioLoader = 0 ;
2014-09-04 07:33:44 +00:00
}
2015-05-29 18:52:43 +00:00
QMutexLocker lock ( & playerMutex ) ;
AudioPlayer * voice = audioPlayer ( ) ;
2014-09-04 07:33:44 +00:00
if ( ! voice ) return ;
for ( int32 i = 0 ; i < AudioVoiceMsgSimultaneously ; + + i ) {
2015-06-30 21:07:05 +00:00
AudioPlayer : : AudioMsg & m ( voice - > _audioData [ i ] ) ;
2014-09-04 07:33:44 +00:00
if ( m . audio = = audio ) {
m . loading = false ;
}
}
}
2015-05-29 18:52:43 +00:00
2015-06-30 21:07:05 +00:00
void AudioPlayerLoaders : : onCancel ( const SongMsgId & song ) {
if ( _song = = song ) {
_song = SongMsgId ( ) ;
delete _songLoader ;
_songLoader = 0 ;
}
QMutexLocker lock ( & playerMutex ) ;
AudioPlayer * voice = audioPlayer ( ) ;
if ( ! voice ) return ;
for ( int32 i = 0 ; i < AudioSongSimultaneously ; + + i ) {
AudioPlayer : : SongMsg & m ( voice - > _songData [ i ] ) ;
if ( m . song = = song ) {
m . loading = false ;
}
}
}
2015-05-29 18:52:43 +00:00
struct AudioCapturePrivate {
2016-02-12 16:35:06 +00:00
AudioCapturePrivate ( )
: device ( 0 )
, fmt ( 0 )
, ioBuffer ( 0 )
, ioContext ( 0 )
, fmtContext ( 0 )
, stream ( 0 )
, codec ( 0 )
, codecContext ( 0 )
, opened ( false )
, srcSamples ( 0 )
, dstSamples ( 0 )
, maxDstSamples ( 0 )
, dstSamplesSize ( 0 )
, fullSamples ( 0 )
, srcSamplesData ( 0 )
, dstSamplesData ( 0 )
, swrContext ( 0 )
, lastUpdate ( 0 )
, levelMax ( 0 )
2016-02-14 19:46:01 +00:00
, dataPos ( 0 )
2016-02-12 16:35:06 +00:00
, waveformMod ( 0 )
, waveformEach ( AudioVoiceMsgFrequency / 100 )
2016-02-14 19:46:01 +00:00
, waveformPeak ( 0 ) {
2015-05-29 18:52:43 +00:00
}
ALCdevice * device ;
AVOutputFormat * fmt ;
uchar * ioBuffer ;
AVIOContext * ioContext ;
AVFormatContext * fmtContext ;
AVStream * stream ;
AVCodec * codec ;
AVCodecContext * codecContext ;
bool opened ;
int32 srcSamples , dstSamples , maxDstSamples , dstSamplesSize , fullSamples ;
uint8_t * * srcSamplesData , * * dstSamplesData ;
SwrContext * swrContext ;
int32 lastUpdate ;
2016-02-12 16:35:06 +00:00
uint16 levelMax ;
2015-05-29 18:52:43 +00:00
QByteArray data ;
int32 dataPos ;
2016-02-12 16:35:06 +00:00
int64 waveformMod , waveformEach ;
uint16 waveformPeak ;
QVector < uchar > waveform ;
2015-05-29 18:52:43 +00:00
static int _read_data ( void * opaque , uint8_t * buf , int buf_size ) {
AudioCapturePrivate * l = reinterpret_cast < AudioCapturePrivate * > ( opaque ) ;
int32 nbytes = qMin ( l - > data . size ( ) - l - > dataPos , int32 ( buf_size ) ) ;
if ( nbytes < = 0 ) {
return 0 ;
}
memcpy ( buf , l - > data . constData ( ) + l - > dataPos , nbytes ) ;
l - > dataPos + = nbytes ;
return nbytes ;
}
static int _write_data ( void * opaque , uint8_t * buf , int buf_size ) {
AudioCapturePrivate * l = reinterpret_cast < AudioCapturePrivate * > ( opaque ) ;
if ( buf_size < = 0 ) return 0 ;
if ( l - > dataPos + buf_size > l - > data . size ( ) ) l - > data . resize ( l - > dataPos + buf_size ) ;
memcpy ( l - > data . data ( ) + l - > dataPos , buf , buf_size ) ;
l - > dataPos + = buf_size ;
return buf_size ;
}
static int64_t _seek_data ( void * opaque , int64_t offset , int whence ) {
AudioCapturePrivate * l = reinterpret_cast < AudioCapturePrivate * > ( opaque ) ;
int32 newPos = - 1 ;
switch ( whence ) {
case SEEK_SET : newPos = offset ; break ;
case SEEK_CUR : newPos = l - > dataPos + offset ; break ;
case SEEK_END : newPos = l - > data . size ( ) + offset ; break ;
}
if ( newPos < 0 ) {
return - 1 ;
}
l - > dataPos = newPos ;
return l - > dataPos ;
}
} ;
AudioCaptureInner : : AudioCaptureInner ( QThread * thread ) : d ( new AudioCapturePrivate ( ) ) {
moveToThread ( thread ) ;
_timer . moveToThread ( thread ) ;
connect ( & _timer , SIGNAL ( timeout ( ) ) , this , SLOT ( onTimeout ( ) ) ) ;
}
AudioCaptureInner : : ~ AudioCaptureInner ( ) {
onStop ( false ) ;
delete d ;
}
void AudioCaptureInner : : onInit ( ) {
}
void AudioCaptureInner : : onStart ( ) {
2016-01-05 06:59:57 +00:00
2015-05-29 18:52:43 +00:00
// Start OpenAL Capture
2015-06-01 11:15:07 +00:00
const ALCchar * dName = alcGetString ( 0 , ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER ) ;
DEBUG_LOG ( ( " Audio Info: Capture device name '%1' " ) . arg ( dName ) ) ;
d - > device = alcCaptureOpenDevice ( dName , AudioVoiceMsgFrequency , AL_FORMAT_MONO16 , AudioVoiceMsgFrequency / 5 ) ;
2015-05-29 18:52:43 +00:00
if ( ! d - > device ) {
LOG ( ( " Audio Error: capture device not present! " ) ) ;
emit error ( ) ;
return ;
}
alcCaptureStart ( d - > device ) ;
if ( ! _checkCaptureError ( d - > device ) ) {
alcCaptureCloseDevice ( d - > device ) ;
d - > device = 0 ;
emit error ( ) ;
return ;
}
// Create encoding context
d - > ioBuffer = ( uchar * ) av_malloc ( AVBlockSize ) ;
2016-01-05 06:59:57 +00:00
2015-05-29 18:52:43 +00:00
d - > ioContext = avio_alloc_context ( d - > ioBuffer , AVBlockSize , 1 , static_cast < void * > ( d ) , & AudioCapturePrivate : : _read_data , & AudioCapturePrivate : : _write_data , & AudioCapturePrivate : : _seek_data ) ;
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
AVOutputFormat * fmt = 0 ;
while ( ( fmt = av_oformat_next ( fmt ) ) ) {
2015-06-27 13:02:00 +00:00
if ( fmt - > name = = qstr ( " opus " ) ) {
2015-05-29 18:52:43 +00:00
break ;
}
}
if ( ! fmt ) {
LOG ( ( " Audio Error: Unable to find opus AVOutputFormat for capture " ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
if ( ( res = avformat_alloc_output_context2 ( & d - > fmtContext , fmt , 0 , 0 ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to avformat_alloc_output_context2 for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
d - > fmtContext - > pb = d - > ioContext ;
d - > fmtContext - > flags | = AVFMT_FLAG_CUSTOM_IO ;
d - > opened = true ;
// Add audio stream
d - > codec = avcodec_find_encoder ( fmt - > audio_codec ) ;
if ( ! d - > codec ) {
LOG ( ( " Audio Error: Unable to avcodec_find_encoder for capture " ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
2015-06-01 11:15:07 +00:00
d - > stream = avformat_new_stream ( d - > fmtContext , d - > codec ) ;
2015-05-29 18:52:43 +00:00
if ( ! d - > stream ) {
LOG ( ( " Audio Error: Unable to avformat_new_stream for capture " ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
d - > stream - > id = d - > fmtContext - > nb_streams - 1 ;
d - > codecContext = d - > stream - > codec ;
av_opt_set_int ( d - > codecContext , " refcounted_frames " , 1 , 0 ) ;
d - > codecContext - > sample_fmt = AV_SAMPLE_FMT_FLTP ;
2015-06-01 11:15:07 +00:00
d - > codecContext - > bit_rate = 64000 ;
d - > codecContext - > channel_layout = AV_CH_LAYOUT_MONO ;
2015-05-29 18:52:43 +00:00
d - > codecContext - > sample_rate = AudioVoiceMsgFrequency ;
d - > codecContext - > channels = 1 ;
if ( d - > fmtContext - > oformat - > flags & AVFMT_GLOBALHEADER ) {
d - > codecContext - > flags | = CODEC_FLAG_GLOBAL_HEADER ;
}
// Open audio stream
2016-03-20 08:16:35 +00:00
if ( ( res = avcodec_open2 ( d - > codecContext , d - > codec , nullptr ) ) < 0 ) {
2015-05-29 18:52:43 +00:00
LOG ( ( " Audio Error: Unable to avcodec_open2 for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
// Alloc source samples
d - > srcSamples = ( d - > codecContext - > codec - > capabilities & CODEC_CAP_VARIABLE_FRAME_SIZE ) ? 10000 : d - > codecContext - > frame_size ;
//if ((res = av_samples_alloc_array_and_samples(&d->srcSamplesData, 0, d->codecContext->channels, d->srcSamples, d->codecContext->sample_fmt, 0)) < 0) {
// LOG(("Audio Error: Unable to av_samples_alloc_array_and_samples for capture, error %1, %2").arg(res).arg(av_make_error_string(err, sizeof(err), res)));
// onStop(false);
// emit error();
// return;
//}
// Using _captured directly
// Prepare resampling
d - > swrContext = swr_alloc ( ) ;
if ( ! d - > swrContext ) {
fprintf ( stderr , " Could not allocate resampler context \n " ) ;
exit ( 1 ) ;
}
av_opt_set_int ( d - > swrContext , " in_channel_count " , d - > codecContext - > channels , 0 ) ;
av_opt_set_int ( d - > swrContext , " in_sample_rate " , d - > codecContext - > sample_rate , 0 ) ;
av_opt_set_sample_fmt ( d - > swrContext , " in_sample_fmt " , AV_SAMPLE_FMT_S16 , 0 ) ;
av_opt_set_int ( d - > swrContext , " out_channel_count " , d - > codecContext - > channels , 0 ) ;
av_opt_set_int ( d - > swrContext , " out_sample_rate " , d - > codecContext - > sample_rate , 0 ) ;
av_opt_set_sample_fmt ( d - > swrContext , " out_sample_fmt " , d - > codecContext - > sample_fmt , 0 ) ;
if ( ( res = swr_init ( d - > swrContext ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to swr_init for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
d - > maxDstSamples = d - > srcSamples ;
if ( ( res = av_samples_alloc_array_and_samples ( & d - > dstSamplesData , 0 , d - > codecContext - > channels , d - > maxDstSamples , d - > codecContext - > sample_fmt , 0 ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to av_samples_alloc_array_and_samples for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
d - > dstSamplesSize = av_samples_get_buffer_size ( 0 , d - > codecContext - > channels , d - > maxDstSamples , d - > codecContext - > sample_fmt , 0 ) ;
// Write file header
if ( ( res = avformat_write_header ( d - > fmtContext , 0 ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to avformat_write_header for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
_timer . start ( 50 ) ;
_captured . clear ( ) ;
_captured . reserve ( AudioVoiceMsgBufferSize ) ;
DEBUG_LOG ( ( " Audio Capture: started! " ) ) ;
}
void AudioCaptureInner : : onStop ( bool needResult ) {
if ( ! _timer . isActive ( ) ) return ; // in onStop() already
_timer . stop ( ) ;
2015-06-01 11:15:07 +00:00
if ( d - > device ) {
alcCaptureStop ( d - > device ) ;
onTimeout ( ) ; // get last data
}
2015-05-29 18:52:43 +00:00
// Write what is left
if ( ! _captured . isEmpty ( ) ) {
int32 fadeSamples = AudioVoiceMsgFade * AudioVoiceMsgFrequency / 1000 , capturedSamples = _captured . size ( ) / sizeof ( short ) ;
if ( ( _captured . size ( ) % sizeof ( short ) ) | | ( d - > fullSamples + capturedSamples < AudioVoiceMsgFrequency ) | | ( capturedSamples < fadeSamples ) ) {
d - > fullSamples = 0 ;
d - > dataPos = 0 ;
d - > data . clear ( ) ;
2016-02-12 16:35:06 +00:00
d - > waveformMod = 0 ;
d - > waveformPeak = 0 ;
d - > waveform . clear ( ) ;
2015-05-29 18:52:43 +00:00
} else {
float64 coef = 1. / fadeSamples , fadedFrom = 0 ;
for ( short * ptr = ( ( short * ) _captured . data ( ) ) + capturedSamples , * end = ptr - fadeSamples ; ptr ! = end ; + + fadedFrom ) {
- - ptr ;
* ptr = qRound ( fadedFrom * coef * * ptr ) ;
}
if ( capturedSamples % d - > srcSamples ) {
int32 s = _captured . size ( ) ;
_captured . resize ( s + ( d - > srcSamples - ( capturedSamples % d - > srcSamples ) ) * sizeof ( short ) ) ;
memset ( _captured . data ( ) + s , 0 , _captured . size ( ) - s ) ;
}
int32 framesize = d - > srcSamples * d - > codecContext - > channels * sizeof ( short ) , encoded = 0 ;
while ( _captured . size ( ) > = encoded + framesize ) {
writeFrame ( encoded , framesize ) ;
encoded + = framesize ;
}
if ( encoded ! = _captured . size ( ) ) {
d - > fullSamples = 0 ;
d - > dataPos = 0 ;
d - > data . clear ( ) ;
2016-02-12 16:35:06 +00:00
d - > waveformMod = 0 ;
d - > waveformPeak = 0 ;
d - > waveform . clear ( ) ;
2015-05-29 18:52:43 +00:00
}
}
}
2016-01-11 15:43:29 +00:00
DEBUG_LOG ( ( " Audio Capture: stopping (need result: %1), size: %2, samples: %3 " ) . arg ( Logs : : b ( needResult ) ) . arg ( d - > data . size ( ) ) . arg ( d - > fullSamples ) ) ;
2015-05-29 18:52:43 +00:00
_captured = QByteArray ( ) ;
// Finish stream
if ( d - > device ) {
av_write_trailer ( d - > fmtContext ) ;
}
QByteArray result = d - > fullSamples ? d - > data : QByteArray ( ) ;
2016-02-12 16:35:06 +00:00
VoiceWaveform waveform ;
2015-05-29 18:52:43 +00:00
qint32 samples = d - > fullSamples ;
2016-02-12 16:35:06 +00:00
if ( samples & & ! d - > waveform . isEmpty ( ) ) {
int64 count = d - > waveform . size ( ) , sum = 0 ;
if ( count > = WaveformSamplesCount ) {
QVector < uint16 > peaks ;
peaks . reserve ( WaveformSamplesCount ) ;
uint16 peak = 0 ;
for ( int32 i = 0 ; i < count ; + + i ) {
uint16 sample = uint16 ( d - > waveform . at ( i ) ) * 256 ;
if ( peak < sample ) {
peak = sample ;
}
sum + = WaveformSamplesCount ;
if ( sum > = count ) {
sum - = count ;
peaks . push_back ( peak ) ;
peak = 0 ;
}
}
int64 sum = std : : accumulate ( peaks . cbegin ( ) , peaks . cend ( ) , 0ULL ) ;
peak = qMax ( int32 ( sum * 1.8 / peaks . size ( ) ) , 2500 ) ;
waveform . resize ( peaks . size ( ) ) ;
for ( int32 i = 0 , l = peaks . size ( ) ; i ! = l ; + + i ) {
waveform [ i ] = char ( qMin ( 31U , uint32 ( qMin ( peaks . at ( i ) , peak ) ) * 31 / peak ) ) ;
}
}
}
2015-05-29 18:52:43 +00:00
if ( d - > device ) {
alcCaptureStop ( d - > device ) ;
alcCaptureCloseDevice ( d - > device ) ;
d - > device = 0 ;
if ( d - > ioContext ) {
av_free ( d - > ioContext ) ;
d - > ioContext = 0 ;
}
if ( d - > codecContext ) {
avcodec_close ( d - > codecContext ) ;
d - > codecContext = 0 ;
}
if ( d - > srcSamplesData ) {
if ( d - > srcSamplesData [ 0 ] ) {
av_freep ( & d - > srcSamplesData [ 0 ] ) ;
}
av_freep ( & d - > srcSamplesData ) ;
}
if ( d - > dstSamplesData ) {
if ( d - > dstSamplesData [ 0 ] ) {
av_freep ( & d - > dstSamplesData [ 0 ] ) ;
}
av_freep ( & d - > dstSamplesData ) ;
}
d - > fullSamples = 0 ;
if ( d - > swrContext ) {
swr_free ( & d - > swrContext ) ;
d - > swrContext = 0 ;
}
if ( d - > opened ) {
avformat_close_input ( & d - > fmtContext ) ;
d - > opened = false ;
d - > ioBuffer = 0 ;
} else if ( d - > ioBuffer ) {
av_free ( d - > ioBuffer ) ;
d - > ioBuffer = 0 ;
}
if ( d - > fmtContext ) {
avformat_free_context ( d - > fmtContext ) ;
d - > fmtContext = 0 ;
}
d - > fmt = 0 ;
d - > stream = 0 ;
d - > codec = 0 ;
d - > lastUpdate = 0 ;
2016-02-12 16:35:06 +00:00
d - > levelMax = 0 ;
2015-05-29 18:52:43 +00:00
d - > dataPos = 0 ;
d - > data . clear ( ) ;
2016-02-12 16:35:06 +00:00
d - > waveformMod = 0 ;
d - > waveformPeak = 0 ;
d - > waveform . clear ( ) ;
2015-05-29 18:52:43 +00:00
}
2016-02-12 16:35:06 +00:00
if ( needResult ) emit done ( result , waveform , samples ) ;
2015-05-29 18:52:43 +00:00
}
void AudioCaptureInner : : onTimeout ( ) {
if ( ! d - > device ) {
_timer . stop ( ) ;
return ;
}
ALint samples ;
alcGetIntegerv ( d - > device , ALC_CAPTURE_SAMPLES , sizeof ( samples ) , & samples ) ;
if ( ! _checkCaptureError ( d - > device ) ) {
onStop ( false ) ;
emit error ( ) ;
return ;
}
if ( samples > 0 ) {
// Get samples from OpenAL
int32 s = _captured . size ( ) , news = s + samples * sizeof ( short ) ;
if ( news / AudioVoiceMsgBufferSize > s / AudioVoiceMsgBufferSize ) {
_captured . reserve ( ( ( news / AudioVoiceMsgBufferSize ) + 1 ) * AudioVoiceMsgBufferSize ) ;
}
_captured . resize ( news ) ;
alcCaptureSamples ( d - > device , ( ALCvoid * ) ( _captured . data ( ) + s ) , samples ) ;
if ( ! _checkCaptureError ( d - > device ) ) {
onStop ( false ) ;
emit error ( ) ;
return ;
}
// Count new recording level and update view
int32 skipSamples = AudioVoiceMsgSkip * AudioVoiceMsgFrequency / 1000 , fadeSamples = AudioVoiceMsgFade * AudioVoiceMsgFrequency / 1000 ;
int32 levelindex = d - > fullSamples + ( s / sizeof ( short ) ) ;
for ( const short * ptr = ( const short * ) ( _captured . constData ( ) + s ) , * end = ( const short * ) ( _captured . constData ( ) + news ) ; ptr < end ; + + ptr , + + levelindex ) {
if ( levelindex > skipSamples ) {
2016-02-12 16:35:06 +00:00
uint16 value = qAbs ( * ptr ) ;
2015-05-29 18:52:43 +00:00
if ( levelindex < skipSamples + fadeSamples ) {
2016-02-12 16:35:06 +00:00
value = qRound ( value * float64 ( levelindex - skipSamples ) / fadeSamples ) ;
}
if ( d - > levelMax < value ) {
d - > levelMax = value ;
2015-05-29 18:52:43 +00:00
}
}
}
qint32 samplesFull = d - > fullSamples + _captured . size ( ) / sizeof ( short ) , samplesSinceUpdate = samplesFull - d - > lastUpdate ;
if ( samplesSinceUpdate > AudioVoiceMsgUpdateView * AudioVoiceMsgFrequency / 1000 ) {
2016-02-12 16:35:06 +00:00
emit update ( d - > levelMax , samplesFull ) ;
2015-05-29 18:52:43 +00:00
d - > lastUpdate = samplesFull ;
2016-02-12 16:35:06 +00:00
d - > levelMax = 0 ;
2015-05-29 18:52:43 +00:00
}
// Write frames
int32 framesize = d - > srcSamples * d - > codecContext - > channels * sizeof ( short ) , encoded = 0 ;
2015-05-30 16:30:47 +00:00
while ( uint32 ( _captured . size ( ) ) > = encoded + framesize + fadeSamples * sizeof ( short ) ) {
2015-05-29 18:52:43 +00:00
writeFrame ( encoded , framesize ) ;
encoded + = framesize ;
}
// Collapse the buffer
if ( encoded > 0 ) {
int32 goodSize = _captured . size ( ) - encoded ;
memmove ( _captured . data ( ) , _captured . constData ( ) + encoded , goodSize ) ;
_captured . resize ( goodSize ) ;
}
} else {
DEBUG_LOG ( ( " Audio Capture: no samples to capture. " ) ) ;
}
}
void AudioCaptureInner : : writeFrame ( int32 offset , int32 framesize ) {
// Prepare audio frame
if ( framesize % sizeof ( short ) ) { // in the middle of a sample
LOG ( ( " Audio Error: Bad framesize in writeFrame() for capture, framesize %1, %2 " ) . arg ( framesize ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
int32 samplesCnt = framesize / sizeof ( short ) ;
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
short * srcSamplesDataChannel = ( short * ) ( _captured . data ( ) + offset ) , * * srcSamplesData = & srcSamplesDataChannel ;
// memcpy(d->srcSamplesData[0], _captured.constData() + offset, framesize);
int32 skipSamples = AudioVoiceMsgSkip * AudioVoiceMsgFrequency / 1000 , fadeSamples = AudioVoiceMsgFade * AudioVoiceMsgFrequency / 1000 ;
if ( d - > fullSamples < skipSamples + fadeSamples ) {
int32 fadedCnt = qMin ( samplesCnt , skipSamples + fadeSamples - d - > fullSamples ) ;
float64 coef = 1. / fadeSamples , fadedFrom = d - > fullSamples - skipSamples ;
2016-02-12 16:35:06 +00:00
short * ptr = srcSamplesDataChannel , * zeroEnd = ptr + qMin ( samplesCnt , qMax ( 0 , skipSamples - d - > fullSamples ) ) , * end = ptr + fadedCnt ;
2015-05-29 18:52:43 +00:00
for ( ; ptr ! = zeroEnd ; + + ptr , + + fadedFrom ) {
* ptr = 0 ;
}
for ( ; ptr ! = end ; + + ptr , + + fadedFrom ) {
* ptr = qRound ( fadedFrom * coef * * ptr ) ;
}
}
2016-02-12 16:35:06 +00:00
d - > waveform . reserve ( d - > waveform . size ( ) + ( samplesCnt / d - > waveformEach ) + 1 ) ;
for ( short * ptr = srcSamplesDataChannel , * end = ptr + samplesCnt ; ptr ! = end ; + + ptr ) {
uint16 value = qAbs ( * ptr ) ;
if ( d - > waveformPeak < value ) {
d - > waveformPeak = value ;
}
if ( + + d - > waveformMod = = d - > waveformEach ) {
d - > waveformMod - = d - > waveformEach ;
d - > waveform . push_back ( uchar ( d - > waveformPeak / 256 ) ) ;
d - > waveformPeak = 0 ;
}
}
2015-05-29 18:52:43 +00:00
// Convert to final format
d - > dstSamples = av_rescale_rnd ( swr_get_delay ( d - > swrContext , d - > codecContext - > sample_rate ) + d - > srcSamples , d - > codecContext - > sample_rate , d - > codecContext - > sample_rate , AV_ROUND_UP ) ;
if ( d - > dstSamples > d - > maxDstSamples ) {
d - > maxDstSamples = d - > dstSamples ;
av_free ( d - > dstSamplesData [ 0 ] ) ;
if ( ( res = av_samples_alloc ( d - > dstSamplesData , 0 , d - > codecContext - > channels , d - > dstSamples , d - > codecContext - > sample_fmt , 0 ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to av_samples_alloc for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
d - > dstSamplesSize = av_samples_get_buffer_size ( 0 , d - > codecContext - > channels , d - > maxDstSamples , d - > codecContext - > sample_fmt , 0 ) ;
}
if ( ( res = swr_convert ( d - > swrContext , d - > dstSamplesData , d - > dstSamples , ( const uint8_t * * ) srcSamplesData , d - > srcSamples ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to swr_convert for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
// Write audio frame
2015-05-30 16:30:47 +00:00
AVPacket pkt ;
memset ( & pkt , 0 , sizeof ( pkt ) ) ; // data and size must be 0;
AVFrame * frame = av_frame_alloc ( ) ;
2015-05-29 18:52:43 +00:00
int gotPacket ;
av_init_packet ( & pkt ) ;
frame - > nb_samples = d - > dstSamples ;
avcodec_fill_audio_frame ( frame , d - > codecContext - > channels , d - > codecContext - > sample_fmt , d - > dstSamplesData [ 0 ] , d - > dstSamplesSize , 0 ) ;
if ( ( res = avcodec_encode_audio2 ( d - > codecContext , & pkt , frame , & gotPacket ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to avcodec_encode_audio2 for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
if ( gotPacket ) {
pkt . stream_index = d - > stream - > index ;
if ( ( res = av_interleaved_write_frame ( d - > fmtContext , & pkt ) ) < 0 ) {
LOG ( ( " Audio Error: Unable to av_interleaved_write_frame for capture, error %1, %2 " ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
onStop ( false ) ;
emit error ( ) ;
return ;
}
}
d - > fullSamples + = samplesCnt ;
2015-05-30 16:30:47 +00:00
av_frame_free ( & frame ) ;
2015-06-01 11:15:07 +00:00
}
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
class FFMpegAttributesReader : public AbstractFFMpegLoader {
2015-06-30 21:07:05 +00:00
public :
2016-02-12 16:35:06 +00:00
FFMpegAttributesReader ( const FileLocation & file , const QByteArray & data ) : AbstractFFMpegLoader ( file , data ) {
2015-06-30 21:07:05 +00:00
}
2015-07-03 08:47:16 +00:00
bool open ( qint64 position = 0 ) {
2016-02-15 07:09:14 +00:00
if ( ! AbstractFFMpegLoader : : open ( ) ) {
2015-06-30 21:07:05 +00:00
return false ;
}
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2016-02-12 16:35:06 +00:00
int videoStreamId = av_find_best_stream ( fmtContext , AVMEDIA_TYPE_VIDEO , - 1 , - 1 , & codec , 0 ) ;
if ( videoStreamId > = 0 ) {
DEBUG_LOG ( ( " Audio Read Error: Found video stream in file '%1', data size '%2', error %3, %4 " ) . arg ( file . name ( ) ) . arg ( data . size ( ) ) . arg ( videoStreamId ) . arg ( av_make_error_string ( err , sizeof ( err ) , streamId ) ) ) ;
2015-06-30 21:07:05 +00:00
return false ;
}
for ( int32 i = 0 , l = fmtContext - > nb_streams ; i < l ; + + i ) {
AVStream * stream = fmtContext - > streams [ i ] ;
if ( stream - > disposition & AV_DISPOSITION_ATTACHED_PIC ) {
const AVPacket & packet ( stream - > attached_pic ) ;
if ( packet . size ) {
bool animated = false ;
QByteArray cover ( ( const char * ) packet . data , packet . size ) , format ;
_cover = App : : readImage ( cover , & format , true , & animated ) ;
if ( ! _cover . isNull ( ) ) {
_coverBytes = cover ;
_coverFormat = format ;
break ;
}
}
}
}
extractMetaData ( fmtContext - > streams [ streamId ] - > metadata ) ;
extractMetaData ( fmtContext - > metadata ) ;
return true ;
}
void trySet ( QString & to , AVDictionary * dict , const char * key ) {
if ( ! to . isEmpty ( ) ) return ;
if ( AVDictionaryEntry * tag = av_dict_get ( dict , key , 0 , 0 ) ) {
to = QString : : fromUtf8 ( tag - > value ) ;
}
}
void extractMetaData ( AVDictionary * dict ) {
trySet ( _title , dict , " title " ) ;
trySet ( _performer , dict , " artist " ) ;
trySet ( _performer , dict , " performer " ) ;
trySet ( _performer , dict , " album_artist " ) ;
2015-09-23 17:43:08 +00:00
//for (AVDictionaryEntry *tag = av_dict_get(dict, "", 0, AV_DICT_IGNORE_SUFFIX); tag; tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX)) {
// const char *key = tag->key;
// const char *value = tag->value;
// QString tmp = QString::fromUtf8(value);
//}
2015-06-30 21:07:05 +00:00
}
int32 format ( ) {
return 0 ;
}
QString title ( ) {
return _title ;
}
2016-01-05 06:59:57 +00:00
2015-06-30 21:07:05 +00:00
QString performer ( ) {
return _performer ;
}
QImage cover ( ) {
return _cover ;
}
QByteArray coverBytes ( ) {
return _coverBytes ;
}
QByteArray coverFormat ( ) {
return _coverFormat ;
}
2015-07-14 19:07:21 +00:00
int readMore ( QByteArray & result , int64 & samplesAdded ) {
2015-06-30 21:07:05 +00:00
DEBUG_LOG ( ( " Audio Read Error: should not call this " ) ) ;
2015-07-14 19:07:21 +00:00
return - 1 ;
2015-06-30 21:07:05 +00:00
}
~ FFMpegAttributesReader ( ) {
}
private :
QString _title , _performer ;
QImage _cover ;
QByteArray _coverBytes , _coverFormat ;
2016-02-12 16:35:06 +00:00
} ;
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
MTPDocumentAttribute audioReadSongAttributes ( const QString & fname , const QByteArray & data , QImage & cover , QByteArray & coverBytes , QByteArray & coverFormat ) {
FFMpegAttributesReader reader ( FileLocation ( StorageFilePartial , fname ) , data ) ;
if ( reader . open ( ) ) {
int32 duration = reader . duration ( ) / reader . frequency ( ) ;
if ( reader . duration ( ) > 0 ) {
cover = reader . cover ( ) ;
coverBytes = reader . coverBytes ( ) ;
coverFormat = reader . coverFormat ( ) ;
2016-03-19 16:55:15 +00:00
return MTP_documentAttributeAudio ( MTP_flags ( MTPDdocumentAttributeAudio : : Flag : : f_title | MTPDdocumentAttributeAudio : : Flag : : f_performer ) , MTP_int ( duration ) , MTP_string ( reader . title ( ) ) , MTP_string ( reader . performer ( ) ) , MTPstring ( ) ) ;
2016-02-12 16:35:06 +00:00
}
}
return MTP_documentAttributeFilename ( MTP_string ( fname ) ) ;
}
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
class FFMpegWaveformCounter : public FFMpegLoader {
public :
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
FFMpegWaveformCounter ( const FileLocation & file , const QByteArray & data ) : FFMpegLoader ( file , data ) {
}
bool open ( qint64 position = 0 ) {
if ( ! FFMpegLoader : : open ( position ) ) {
return false ;
2015-06-30 21:07:05 +00:00
}
2016-02-12 16:35:06 +00:00
QByteArray buffer ;
buffer . reserve ( AudioVoiceMsgBufferSize ) ;
int64 countbytes = sampleSize * duration ( ) , processed = 0 , sumbytes = 0 ;
if ( duration ( ) < WaveformSamplesCount ) {
return false ;
}
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
QVector < uint16 > peaks ;
peaks . reserve ( WaveformSamplesCount ) ;
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
int32 fmt = format ( ) ;
uint16 peak = 0 ;
while ( processed < countbytes ) {
buffer . resize ( 0 ) ;
int64 samples = 0 ;
int res = readMore ( buffer , samples ) ;
if ( res < 0 ) {
break ;
}
if ( buffer . isEmpty ( ) ) {
continue ;
}
const char * data = buffer . data ( ) ;
if ( fmt = = AL_FORMAT_MONO8 | | fmt = = AL_FORMAT_STEREO8 ) {
2016-02-14 19:46:01 +00:00
for ( int32 i = 0 , l = buffer . size ( ) ; i + int32 ( sizeof ( uchar ) ) < = l ; ) {
2016-02-12 16:35:06 +00:00
uint16 sample = qAbs ( ( int32 ( * ( uchar * ) ( data + i ) ) - 128 ) * 256 ) ;
if ( peak < sample ) {
peak = sample ;
}
i + = sizeof ( uchar ) ;
sumbytes + = WaveformSamplesCount ;
if ( sumbytes > = countbytes ) {
sumbytes - = countbytes ;
peaks . push_back ( peak ) ;
peak = 0 ;
}
}
} else if ( fmt = = AL_FORMAT_MONO16 | | fmt = = AL_FORMAT_STEREO16 ) {
2016-02-14 19:46:01 +00:00
for ( int32 i = 0 , l = buffer . size ( ) ; i + int32 ( sizeof ( uint16 ) ) < = l ; ) {
2016-02-12 16:35:06 +00:00
uint16 sample = qAbs ( int32 ( * ( int16 * ) ( data + i ) ) ) ;
if ( peak < sample ) {
peak = sample ;
}
i + = sizeof ( uint16 ) ;
sumbytes + = sizeof ( uint16 ) * WaveformSamplesCount ;
if ( sumbytes > = countbytes ) {
sumbytes - = countbytes ;
peaks . push_back ( peak ) ;
peak = 0 ;
}
}
}
processed + = sampleSize * samples ;
2015-06-30 21:07:05 +00:00
}
2016-02-12 16:35:06 +00:00
if ( sumbytes > 0 & & peaks . size ( ) < WaveformSamplesCount ) {
peaks . push_back ( peak ) ;
2015-06-30 21:07:05 +00:00
}
2016-02-12 16:35:06 +00:00
if ( peaks . isEmpty ( ) ) {
return false ;
}
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
int64 sum = std : : accumulate ( peaks . cbegin ( ) , peaks . cend ( ) , 0ULL ) ;
peak = qMax ( int32 ( sum * 1.8 / peaks . size ( ) ) , 2500 ) ;
2015-06-30 21:07:05 +00:00
2016-02-12 16:35:06 +00:00
result . resize ( peaks . size ( ) ) ;
for ( int32 i = 0 , l = peaks . size ( ) ; i ! = l ; + + i ) {
result [ i ] = char ( qMin ( 31U , uint32 ( qMin ( peaks . at ( i ) , peak ) ) * 31 / peak ) ) ;
2015-06-30 21:07:05 +00:00
}
2016-02-12 16:35:06 +00:00
return true ;
}
const VoiceWaveform & waveform ( ) const {
return result ;
2015-06-30 21:07:05 +00:00
}
2016-02-12 16:35:06 +00:00
~ FFMpegWaveformCounter ( ) {
}
private :
VoiceWaveform result ;
2015-06-30 21:07:05 +00:00
} ;
2016-02-12 16:35:06 +00:00
VoiceWaveform audioCountWaveform ( const FileLocation & file , const QByteArray & data ) {
FFMpegWaveformCounter counter ( file , data ) ;
if ( counter . open ( ) ) {
return counter . waveform ( ) ;
2015-06-30 21:07:05 +00:00
}
2016-02-12 16:35:06 +00:00
return VoiceWaveform ( ) ;
2015-06-30 21:07:05 +00:00
}