Linux: Alsa backend for the audio plugins. Didn't succeed in reducing latency though, but hopefully will work better than libao - shouldn't hang weirdly at underruns, I hope, like libao does.
git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@4240 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
e0dfe17ca4
commit
987a6185e6
|
@ -64,8 +64,7 @@ public:
|
|||
|
||||
#else
|
||||
public:
|
||||
AOSound(CMixer *mixer) :
|
||||
SoundStream(mixer) {}
|
||||
AOSound(CMixer *mixer) : SoundStream(mixer) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include "AlsaSoundStream.h"
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
#define BUFFER_SIZE_BYTES (BUFFER_SIZE*2*2)
|
||||
|
||||
AlsaSound::AlsaSound(CMixer *mixer) : SoundStream(mixer), thread_data(0), handle(NULL)
|
||||
{
|
||||
mix_buffer = new u8[BUFFER_SIZE_BYTES];
|
||||
}
|
||||
|
||||
AlsaSound::~AlsaSound()
|
||||
{
|
||||
delete [] mix_buffer;
|
||||
}
|
||||
|
||||
static void *ThreadTrampoline(void *args)
|
||||
{
|
||||
reinterpret_cast<AlsaSound *>(args)->SoundLoop();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool AlsaSound::Start()
|
||||
{
|
||||
thread = new Common::Thread(&ThreadTrampoline, this);
|
||||
thread_data = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AlsaSound::Stop()
|
||||
{
|
||||
thread_data = 1;
|
||||
delete thread;
|
||||
thread = NULL;
|
||||
}
|
||||
|
||||
void AlsaSound::Update()
|
||||
{
|
||||
// don't need to do anything here.
|
||||
}
|
||||
|
||||
// Called on audio thread.
|
||||
void AlsaSound::SoundLoop()
|
||||
{
|
||||
AlsaInit();
|
||||
while (!thread_data)
|
||||
{
|
||||
int frames_to_deliver = 512;
|
||||
m_mixer->Mix(reinterpret_cast<short *>(mix_buffer), frames_to_deliver);
|
||||
int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver);
|
||||
if (rc == -EPIPE)
|
||||
{
|
||||
// Underrun
|
||||
snd_pcm_prepare(handle);
|
||||
}
|
||||
else if (rc < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc));
|
||||
}
|
||||
}
|
||||
AlsaShutdown();
|
||||
thread_data = 2;
|
||||
}
|
||||
|
||||
bool AlsaSound::AlsaInit()
|
||||
{
|
||||
unsigned int sample_rate = m_mixer->GetSampleRate();
|
||||
int err;
|
||||
int dir;
|
||||
snd_pcm_sw_params_t *swparams;
|
||||
snd_pcm_hw_params_t *hwparams;
|
||||
|
||||
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_alloca(&hwparams);
|
||||
|
||||
err = snd_pcm_hw_params_any(handle, hwparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is weird - if I do pass in a pointer to a variable, like the header wants me to,
|
||||
// the sample rate goes mad. It seems that the alsa header doesn't match the library we link in :(
|
||||
// If anyone know why, i'd appreciate if you let me know - ector.
|
||||
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, (unsigned int *)sample_rate, &dir);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params_set_channels(handle, hwparams, 2);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_hw_params(handle, hwparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_pcm_sw_params_alloca(&swparams);
|
||||
|
||||
err = snd_pcm_sw_params_current(handle, swparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params_set_avail_min(handle, swparams, BUFFER_SIZE);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot set avail min: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_sw_params(handle, swparams);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = snd_pcm_prepare(handle);
|
||||
if (err < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void AlsaSound::AlsaShutdown()
|
||||
{
|
||||
if (handle != NULL)
|
||||
{
|
||||
snd_pcm_drop(handle);
|
||||
snd_pcm_close(handle);
|
||||
handle = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright (C) 2003 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifndef _ALSA_SOUND_STREAM_H
|
||||
#define _ALSA_SOUND_STREAM_H
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "Common.h"
|
||||
#include "SoundStream.h"
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
#define HAVE_ALSA 1
|
||||
|
||||
class AlsaSound : public SoundStream
|
||||
{
|
||||
#if defined(HAVE_ALSA) && HAVE_ALSA
|
||||
public:
|
||||
AlsaSound(CMixer *mixer);
|
||||
virtual ~AlsaSound();
|
||||
|
||||
virtual bool Start();
|
||||
virtual void SoundLoop();
|
||||
virtual void Stop();
|
||||
|
||||
static bool isValid() {
|
||||
return true;
|
||||
}
|
||||
virtual bool usesMixer() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Update();
|
||||
|
||||
private:
|
||||
bool AlsaInit();
|
||||
void AlsaShutdown();
|
||||
|
||||
u8 *mix_buffer;
|
||||
Common::Thread *thread;
|
||||
// 0 = continue
|
||||
// 1 = shutdown
|
||||
// 2 = done shutting down.
|
||||
volatile int thread_data;
|
||||
|
||||
snd_pcm_t *handle;
|
||||
#else
|
||||
public:
|
||||
AlsaSound(CMixer *mixer) : SoundStream(mixer) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
#include "Mixer.h"
|
||||
#include "DSoundStream.h"
|
||||
#include "AOSoundStream.h"
|
||||
#include "AlsaSoundStream.h"
|
||||
#include "NullSoundStream.h"
|
||||
#include "OpenALStream.h"
|
||||
|
||||
|
@ -26,6 +27,7 @@ namespace AudioCommon
|
|||
{
|
||||
SoundStream *InitSoundStream(CMixer *mixer)
|
||||
{
|
||||
// This looks evil.
|
||||
if (!mixer)
|
||||
mixer = new CMixer();
|
||||
|
||||
|
@ -33,11 +35,14 @@ namespace AudioCommon
|
|||
if (backend == BACKEND_DIRECTSOUND && DSound::isValid()) soundStream = new DSound(mixer, g_dspInitialize.hWnd);
|
||||
if (backend == BACKEND_AOSOUND && AOSound::isValid()) soundStream = new AOSound(mixer);
|
||||
if (backend == BACKEND_OPENAL && OpenALStream::isValid()) soundStream = new OpenALStream(mixer);
|
||||
if (backend == BACKEND_ALSA && AlsaSound::isValid()) soundStream = new AlsaSound(mixer);
|
||||
if (backend == BACKEND_NULL && NullSound::isValid()) soundStream = new NullSound(mixer);
|
||||
|
||||
if (soundStream != NULL) {
|
||||
if (soundStream != NULL)
|
||||
{
|
||||
ac_Config.Update();
|
||||
if (soundStream->Start()) {
|
||||
if (soundStream->Start())
|
||||
{
|
||||
// Start the sound recording
|
||||
/*
|
||||
if (ac_Config.record) {
|
||||
|
@ -81,6 +86,7 @@ namespace AudioCommon
|
|||
if (DSound::isValid()) backends.push_back(BACKEND_DIRECTSOUND);
|
||||
if (AOSound::isValid()) backends.push_back(BACKEND_AOSOUND);
|
||||
if (OpenALStream::isValid()) backends.push_back(BACKEND_OPENAL);
|
||||
if (AlsaSound::isValid()) backends.push_back(BACKEND_ALSA);
|
||||
if (NullSound::isValid()) backends.push_back(BACKEND_NULL);
|
||||
|
||||
return backends;
|
||||
|
|
|
@ -25,7 +25,9 @@
|
|||
#define BACKEND_DIRECTSOUND "DSound"
|
||||
#define BACKEND_AOSOUND "AOSound"
|
||||
#define BACKEND_OPENAL "OpenAL"
|
||||
#define BACKEND_ALSA "ALSA"
|
||||
#define BACKEND_NULL "NullSound"
|
||||
|
||||
struct AudioCommonConfig
|
||||
{
|
||||
bool m_EnableDTKMusic;
|
||||
|
|
|
@ -18,4 +18,7 @@ if acenv['HAVE_OPENAL']:
|
|||
if acenv['HAVE_AO']:
|
||||
files += [ 'AOSoundStream.cpp' ]
|
||||
|
||||
# TODO: make conditional?
|
||||
files += [ 'AlsaSoundStream.cpp' ]
|
||||
|
||||
acenv.StaticLibrary(env['local_libs'] + 'audiocommon', files)
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
#define HAVE_WX 1
|
||||
#define HAVE_SFML 1
|
||||
#define HAVE_OPENAL 1
|
||||
#define HAVE_ALSA 1
|
||||
|
||||
// it is VERY DANGEROUS to mix _SECURE_SCL=0 and _SECURE_SCL=1 compiled libraries.
|
||||
// You will get bizarre crash bugs whenever you use STL.
|
||||
|
@ -110,6 +111,11 @@
|
|||
#define POSIX 1
|
||||
#define MAX_PATH 260
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define HAVE_ALSA 0
|
||||
#else
|
||||
#define HAVE_ALSA 1
|
||||
#endif
|
||||
// Windows compatibility
|
||||
#define __forceinline inline __attribute__((always_inline))
|
||||
|
||||
|
|
|
@ -120,11 +120,13 @@ void CopyEFB(const BPCmd &bp, const EFBRectangle &rc, const u32 &address, const
|
|||
{
|
||||
// bpmem.zcontrol.pixel_format to PIXELFMT_Z24 is when the game wants to copy from ZBuffer (Zbuffer uses 24-bit Format)
|
||||
if (!g_Config.bEFBCopyDisable)
|
||||
{
|
||||
if (g_Config.bCopyEFBToRAM) // To RAM
|
||||
TextureConverter::EncodeToRam(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
|
||||
else // To OGL Texture
|
||||
TextureMngr::CopyRenderTargetToTexture(address, fromZBuffer, isIntensityFmt, copyfmt, scaleByHalf, rc);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderToXFB(const BPCmd &bp, const EFBRectangle &rc, const float &yScale, const float &xfbLines, u32 xfbAddr, const u32 &dstWidth, const u32 &dstHeight)
|
||||
{
|
||||
|
|
|
@ -75,6 +75,8 @@ struct CachedDisplayList
|
|||
frame_count = frameCount;
|
||||
}
|
||||
|
||||
bool uncachable; // if set, this DL will always be interpreted. This gets set if hash ever changes.
|
||||
|
||||
int pass;
|
||||
u32 dl_hash;
|
||||
|
||||
|
@ -87,8 +89,6 @@ struct CachedDisplayList
|
|||
|
||||
int frame_count;
|
||||
|
||||
bool uncachable; // if set, this DL will always be interpreted. This gets set if hash ever changes.
|
||||
|
||||
// ... Something containing cached vertex buffers here ...
|
||||
|
||||
// Compile the commands themselves down to native code.
|
||||
|
@ -159,12 +159,12 @@ bool AnalyzeAndRunDisplayList(u32 address, int size, CachedDisplayList *dl)
|
|||
// Execute
|
||||
u32 Cmd2 = DataReadU32();
|
||||
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
||||
u32 address = Cmd2 & 0xFFFF;
|
||||
u32 xf_address = Cmd2 & 0xFFFF;
|
||||
// TODO - speed this up. pshufb?
|
||||
u32 data_buffer[16];
|
||||
for (int i = 0; i < transfer_size; i++)
|
||||
data_buffer[i] = DataReadU32();
|
||||
LoadXFReg(transfer_size, address, data_buffer);
|
||||
LoadXFReg(transfer_size, xf_address, data_buffer);
|
||||
INCSTAT(stats.thisFrame.numXFLoads);
|
||||
|
||||
// Analyze
|
||||
|
@ -311,13 +311,13 @@ bool CompileAndRunDisplayList(u32 address, int size, CachedDisplayList *dl)
|
|||
// Execute
|
||||
u32 Cmd2 = DataReadU32();
|
||||
int transfer_size = ((Cmd2 >> 16) & 15) + 1;
|
||||
u32 address = Cmd2 & 0xFFFF;
|
||||
u32 xf_address = Cmd2 & 0xFFFF;
|
||||
// TODO - speed this up. pshufb?
|
||||
u8 *real_data_buffer = AllocStaticData(4 * transfer_size);
|
||||
u32 *data_buffer = (u32 *)real_data_buffer;
|
||||
for (int i = 0; i < transfer_size; i++)
|
||||
data_buffer[i] = DataReadU32();
|
||||
LoadXFReg(transfer_size, address, data_buffer);
|
||||
LoadXFReg(transfer_size, xf_address, data_buffer);
|
||||
INCSTAT(stats.thisFrame.numXFLoads);
|
||||
|
||||
// Compile
|
||||
|
|
Loading…
Reference in New Issue