Fix sound playback on Linux by enabling fix #1302 for Linux platform (#1749)

close #1302 
close #1097 
close #907
This commit is contained in:
Konstantin Dmitriev 2018-05-25 08:28:41 +07:00 committed by shun-iwasawa
parent 62f1df91c9
commit a9c425317e
6 changed files with 11 additions and 1862 deletions

View file

@ -1,551 +0,0 @@
#include "tsound_t.h"
#include "texception.h"
#include "tthread.h"
#include "tthreadmessage.h"
#include <errno.h>
#include <unistd.h>
#include <queue>
#include <set>
#include <SDL2/SDL.h>
using namespace std;
//==============================================================================
namespace {
TThread::Mutex MutexOut;
}
namespace {
struct MyData {
char *entireFileBuffer;
int totalPacketCount;
int fileByteCount;
// UInt32 maxPacketSize;
// UInt64 packetOffset;
int byteOffset;
bool m_doNotify;
void *sourceBuffer;
// AudioConverterRef converter;
TSoundOutputDeviceImp *imp;
bool isLooping;
MyData()
: entireFileBuffer(0)
, totalPacketCount(0)
, fileByteCount(0)
, /*maxPacketSize(0), packetOffset(0),*/ byteOffset(0)
, sourceBuffer(0)
, isLooping(false)
, imp(0)
, m_doNotify(true) {}
};
}
class TSoundOutputDeviceImp {
public:
bool m_isPlaying;
bool m_looped;
TSoundTrackFormat m_currentFormat;
std::set<int> m_supportedRate;
bool m_opened;
struct MyData *m_data;
int m_volume;
TSoundOutputDeviceImp()
: m_isPlaying(false)
, m_looped(false)
, m_supportedRate()
, m_opened(false)
, m_data(NULL)
, m_volume(SDL_MIX_MAXVOLUME){};
std::set<TSoundOutputDeviceListener *> m_listeners;
~TSoundOutputDeviceImp(){};
bool doOpenDevice(const TSoundTrackFormat &format);
bool doStopDevice();
void play(const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop,
bool scrubbing);
};
//-----------------------------------------------------------------------------
namespace {
class PlayCompletedMsg : public TThread::Message {
std::set<TSoundOutputDeviceListener *> m_listeners;
MyData *m_data;
public:
PlayCompletedMsg(MyData *data) : m_data(data) {}
TThread::Message *clone() const { return new PlayCompletedMsg(*this); }
void onDeliver() {
if (m_data->imp) {
if (m_data->m_doNotify == false) return;
m_data->m_doNotify = false;
std::set<TSoundOutputDeviceListener *>::iterator it =
m_data->imp->m_listeners.begin();
for (; it != m_data->imp->m_listeners.end(); ++it)
(*it)->onPlayCompleted();
if (m_data->imp->m_isPlaying) m_data->imp->doStopDevice();
}
}
};
}
#define checkStatus(err) \
if (err) { \
printf("Error: 0x%x -> %s: %d\n", (int)err, __FILE__, __LINE__); \
fflush(stdout); \
}
extern "C" {
static void sdl_fill_audio(void *udata, Uint8 *stream, int len) {
TSoundOutputDeviceImp *_this = (TSoundOutputDeviceImp *)udata;
MyData *myData = _this->m_data;
/* Only play if we have data left */
if (myData == NULL) return;
{
// TThread::ScopedLock sl(MutexOut);
if (myData->imp->m_isPlaying == false) return;
}
int audio_len = myData->fileByteCount - myData->byteOffset;
if (audio_len <= 0) {
delete[] myData->entireFileBuffer;
myData->entireFileBuffer = 0;
#if 0
{
TThread::ScopedLock sl(MutexOut);
*(myData->isPlaying) = false; //questo lo faccio nel main thread
}
#endif
PlayCompletedMsg(myData).send();
return;
}
/* Mix as much data as possible */
len = min(audio_len, len);
SDL_memset(stream, 0, len);
SDL_MixAudio(stream, (Uint8 *)myData->entireFileBuffer + myData->byteOffset,
len, _this->m_volume);
myData->byteOffset += len;
}
} // extern "C"
bool TSoundOutputDeviceImp::doOpenDevice(const TSoundTrackFormat &format) {
SDL_AudioSpec wanted;
static bool first = true; // TODO: should be shared with InputDevice
if (first) {
SDL_Init(SDL_INIT_AUDIO);
first = false;
}
if (m_opened) {
SDL_CloseAudio();
// we'll just reopen right away
}
wanted.freq = format.m_sampleRate;
switch (format.m_bitPerSample) {
case 8:
wanted.format = AUDIO_S8;
break;
case 16:
wanted.format = AUDIO_S16;
break;
default:
throw TSoundDeviceException(TSoundDeviceException::UnableOpenDevice,
"invalid bits per sample");
return false;
}
wanted.channels = format.m_channelCount; /* 1 = mono, 2 = stereo */
wanted.samples = 1024; /* Good low-latency value for callback */
wanted.callback = sdl_fill_audio;
wanted.userdata = this;
/* Open the audio device, forcing the desired format */
if (SDL_OpenAudio(&wanted, NULL) < 0) {
std::string msg("Couldn't open audio: ");
msg += SDL_GetError();
throw TSoundDeviceException(TSoundDeviceException::UnableOpenDevice, msg);
return false;
}
m_opened = true;
return m_opened;
}
//==============================================================================
TSoundOutputDevice::TSoundOutputDevice() : m_imp(new TSoundOutputDeviceImp) {
#if 0
try {
supportsVolume();
} catch (TSoundDeviceException &e) {
throw TSoundDeviceException(e.getType(), e.getMessage());
}
#endif
}
//------------------------------------------------------------------------------
TSoundOutputDevice::~TSoundOutputDevice() {
stop();
close();
}
//------------------------------------------------------------------------------
bool TSoundOutputDevice::installed() { return true; }
//------------------------------------------------------------------------------
bool TSoundOutputDevice::open(const TSoundTrackP &st) {
if (!m_imp->doOpenDevice(st->getFormat()))
throw TSoundDeviceException(
TSoundDeviceException::UnableOpenDevice,
"Problem to open the output device setting some params");
return true;
}
//------------------------------------------------------------------------------
bool TSoundOutputDevice::close() {
stop();
m_imp->m_opened = false;
return true;
}
//------------------------------------------------------------------------------
void TSoundOutputDevice::play(const TSoundTrackP &st, TINT32 s0, TINT32 s1,
bool loop, bool scrubbing) {
// TThread::ScopedLock sl(MutexOut);
int lastSample = st->getSampleCount() - 1;
notLessThan(0, s0);
notLessThan(0, s1);
notMoreThan(lastSample, s0);
notMoreThan(lastSample, s1);
if (s0 > s1) {
#ifdef DEBUG
cout << "s0 > s1; reorder" << endl;
#endif
swap(s0, s1);
}
if (isPlaying()) {
#ifdef DEBUG
cout << "is playing, stop it!" << endl;
#endif
stop();
}
m_imp->play(st, s0, s1, loop, scrubbing);
}
//------------------------------------------------------------------------------
void TSoundOutputDeviceImp::play(const TSoundTrackP &st, TINT32 s0, TINT32 s1,
bool loop, bool scrubbing) {
if (!doOpenDevice(st->getFormat())) return;
MyData *myData = new MyData();
myData->imp = this;
myData->totalPacketCount = s1 - s0;
myData->fileByteCount = (s1 - s0) * st->getSampleSize();
myData->entireFileBuffer = new char[myData->fileByteCount];
memcpy(myData->entireFileBuffer, st->getRawData() + s0 * st->getSampleSize(),
myData->fileByteCount);
// myData->maxPacketSize = fileASBD.mFramesPerPacket *
// fileASBD.mBytesPerFrame;
{
// TThread::ScopedLock sl(MutexOut);
m_isPlaying = true;
}
myData->isLooping = loop;
// cout << "total packet count = " << myData->totalPacketCount <<endl;
// cout << "filebytecount " << myData->fileByteCount << endl;
m_data = myData;
SDL_PauseAudio(0);
}
//------------------------------------------------------------------------------
bool TSoundOutputDeviceImp::doStopDevice() {
SDL_PauseAudio(1);
m_isPlaying = false;
delete m_data;
m_data = NULL;
SDL_CloseAudio();
m_opened = false;
return true;
}
//------------------------------------------------------------------------------
void TSoundOutputDevice::stop() {
// TThread::ScopedLock sl(MutexOut);
if (m_imp->m_opened == false) return;
// TThread::ScopedLock sl(MutexOut);
m_imp->doStopDevice();
}
//------------------------------------------------------------------------------
void TSoundOutputDevice::attach(TSoundOutputDeviceListener *listener) {
m_imp->m_listeners.insert(listener);
}
//------------------------------------------------------------------------------
void TSoundOutputDevice::detach(TSoundOutputDeviceListener *listener) {
m_imp->m_listeners.erase(listener);
}
#if 0
//------------------------------------------------------------------------------
double TSoundOutputDevice::getVolume()
{
if (!m_imp->m_opened)
m_imp->doOpenDevice();
double vol = m_volume / SDL_MIX_MAXVOLUME;
return (vol < 0. ? 0. : vol);
}
//------------------------------------------------------------------------------
bool TSoundOutputDevice::setVolume(double volume)
{
Float32 vol = volume;
m_imp->m_volume = (int)(volume * SDL_MIX_MAXVOLUME);
return true;
}
//------------------------------------------------------------------------------
bool TSoundOutputDevice::supportsVolume()
{
return true;
}
#endif
//------------------------------------------------------------------------------
bool TSoundOutputDevice::isPlaying() const {
// TThread::ScopedLock sl(MutexOut);
// TODO: handle actually queuing items?
return m_imp->m_isPlaying;
}
//------------------------------------------------------------------------------
bool TSoundOutputDevice::isAllQueuedItemsPlayed() {
// TThread::ScopedLock sl(MutexOut);
return m_imp->m_data == NULL;
}
//------------------------------------------------------------------------------
bool TSoundOutputDevice::isLooping() {
// TThread::ScopedLock sl(MutexOut);
return m_imp->m_looped;
}
//------------------------------------------------------------------------------
void TSoundOutputDevice::setLooping(bool loop) {
// TThread::ScopedLock sl(MutexOut);
m_imp->m_looped = loop;
}
//------------------------------------------------------------------------------
TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(TUINT32 sampleRate,
int channelCount,
int bitPerSample) {
if (bitPerSample > 16) bitPerSample = 16;
// not sure SDL supports more than 2 channels
if (channelCount > 2) channelCount = 2;
TSoundTrackFormat fmt(sampleRate, bitPerSample, channelCount, true);
return fmt;
}
//------------------------------------------------------------------------------
TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(
const TSoundTrackFormat &format) {
#if 0
try {
#endif
return getPreferredFormat(format.m_sampleRate, format.m_channelCount,
format.m_bitPerSample);
#if 0
}
catch (TSoundDeviceException &e) {
throw TSoundDeviceException( e.getType(), e.getMessage());
}
#endif
}
//==============================================================================
//==============================================================================
// REGISTRAZIONE
//==============================================================================
//==============================================================================
class TSoundInputDeviceImp {
public:
// ALport m_port;
bool m_stopped;
bool m_isRecording;
bool m_oneShotRecording;
long m_recordedSampleCount;
TSoundTrackFormat m_currentFormat;
TSoundTrackP m_st;
std::set<int> m_supportedRate;
TThread::Executor m_executor;
TSoundInputDeviceImp()
: m_stopped(false)
, m_isRecording(false)
// , m_port(NULL)
, m_oneShotRecording(false)
, m_recordedSampleCount(0)
, m_st(0)
, m_supportedRate(){};
~TSoundInputDeviceImp(){};
bool doOpenDevice(const TSoundTrackFormat &format,
TSoundInputDevice::Source devType);
};
bool TSoundInputDeviceImp::doOpenDevice(const TSoundTrackFormat &format,
TSoundInputDevice::Source devType) {
return true;
}
//==============================================================================
class RecordTask : public TThread::Runnable {
public:
TSoundInputDeviceImp *m_devImp;
int m_ByteToSample;
RecordTask(TSoundInputDeviceImp *devImp, int numByte)
: TThread::Runnable(), m_devImp(devImp), m_ByteToSample(numByte){};
~RecordTask(){};
void run();
};
void RecordTask::run() {}
//==============================================================================
TSoundInputDevice::TSoundInputDevice() : m_imp(new TSoundInputDeviceImp) {}
//------------------------------------------------------------------------------
TSoundInputDevice::~TSoundInputDevice() {
#if 0
if(m_imp->m_port)
alClosePort(m_imp->m_port);
delete m_imp;
#endif
}
//------------------------------------------------------------------------------
bool TSoundInputDevice::installed() {
#if 0
if (alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, 0, 0, 0, 0) <= 0)
return false;
#endif
return true;
}
//------------------------------------------------------------------------------
void TSoundInputDevice::record(const TSoundTrackFormat &format,
TSoundInputDevice::Source type) {}
//------------------------------------------------------------------------------
void TSoundInputDevice::record(const TSoundTrackP &st,
TSoundInputDevice::Source type) {}
//------------------------------------------------------------------------------
TSoundTrackP TSoundInputDevice::stop() {
TSoundTrackP st;
return st;
}
//------------------------------------------------------------------------------
double TSoundInputDevice::getVolume() { return 0.0; }
//------------------------------------------------------------------------------
bool TSoundInputDevice::setVolume(double volume) { return true; }
//------------------------------------------------------------------------------
bool TSoundInputDevice::supportsVolume() { return true; }
//------------------------------------------------------------------------------
TSoundTrackFormat TSoundInputDevice::getPreferredFormat(TUINT32 sampleRate,
int channelCount,
int bitPerSample) {
TSoundTrackFormat fmt;
return fmt;
}
//------------------------------------------------------------------------------
TSoundTrackFormat TSoundInputDevice::getPreferredFormat(
const TSoundTrackFormat &format) {
#if 0
try {
#endif
return getPreferredFormat(format.m_sampleRate, format.m_channelCount,
format.m_bitPerSample);
#if 0
}
catch (TSoundDeviceException &e) {
throw TSoundDeviceException( e.getType(), e.getMessage());
}
#endif
}
//------------------------------------------------------------------------------
bool TSoundInputDevice::isRecording() { return m_imp->m_isRecording; }

File diff suppressed because it is too large Load diff

View file

@ -372,7 +372,7 @@ Returns true if on the machine there is an audio card installed correctly
bool isFormatSupported(const TSoundTrackFormat &);
#ifdef MACOSX
#ifndef _WIN32
//! Returns true if is possible to change volume setting on current input
//! interface
bool supportsVolume();
@ -412,7 +412,7 @@ interaction between sound and mouse
//! Returns if the device is busy with a playback
bool isPlaying() const;
#ifndef MACOSX
#ifdef _WIN32
//! Return true if the playback of all soundtracks is ended.
bool isAllQueuedItemsPlayed();
#endif

View file

@ -252,18 +252,12 @@ if(BUILD_TARGET_WIN)
)
elseif(BUILD_TARGET_APPLE)
set(SOURCES ${SOURCES}
../common/tsound/tsound_mac.cpp
../common/tsound/tsound_qt.cpp
)
elseif(BUILD_TARGET_UNIX)
if(SDL_LIB_FOUND)
set(SOURCES ${SOURCES}
../common/tsound/tsound_sdl.cpp
../common/tsound/tsound_qt.cpp
)
else()
set(SOURCES ${SOURCES}
../common/tsound/tsound_x.cpp
)
endif()
endif()
add_translation(tnzcore ${HEADERS} ${SOURCES})

View file

@ -788,7 +788,7 @@ void TXshSoundColumn::updateFrameRate(double fps) {
void TXshSoundColumn::setVolume(double value) {
m_volume = tcrop<double>(value, 0.0, 1.0);
if (m_player && m_player->isPlaying())
#ifdef MACOSX
#ifndef _WIN32
m_player->setVolume(m_volume);
#else
stop();
@ -808,7 +808,7 @@ void TXshSoundColumn::play(TSoundTrackP soundtrack, int s0, int s1, bool loop) {
if (m_player) {
try {
#ifdef MACOSX
#ifndef _WIN32
m_player->prepareVolume(m_volume);
TSoundTrackP mixedTrack = soundtrack;
#else
@ -818,7 +818,7 @@ void TXshSoundColumn::play(TSoundTrackP soundtrack, int s0, int s1, bool loop) {
#endif
m_player->play(mixedTrack, s0, s1, loop);
m_currentPlaySoundTrack = mixedTrack;
#ifndef MACOSX
#ifdef _WIN32
m_timer.start();
#endif
} catch (TSoundDeviceException &) {
@ -934,7 +934,7 @@ void TXshSoundColumn::clear() {
//-----------------------------------------------------------------------------
void TXshSoundColumn::onTimerOut() {
#ifndef MACOSX
#ifdef _WIN32
if (m_player && m_player->isAllQueuedItemsPlayed()) stop();
#endif
}
@ -1002,7 +1002,7 @@ TSoundTrackP TXshSoundColumn::getOverallSoundTrack(int fromFrame, int toFrame,
// We prefer to have 22050 as a maximum sampleRate (to avoid crashes or
// another issues)
#ifndef MACOSX
#ifdef _WIN32
if (format.m_sampleRate >= 44100) format.m_sampleRate = 22050;
#else
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());