RageSound_DSound_Software::RageSound_DSound_Software()
{
	shutdown_mixer_thread = false;
	pcm = NULL;

	/* If we're emulated, we're better off with the WaveOut driver; DS
	 * emulation tends to be desynced. */
	if( ds.IsEmulated() )
		RageException::ThrowNonfatal( "Driver unusable (emulated device)" );

	max_writeahead = safe_writeahead;
	if( PREFSMAN->m_iSoundWriteAhead )
		max_writeahead = PREFSMAN->m_iSoundWriteAhead;

	/* Create a DirectSound stream, but don't force it into hardware. */
	pcm = new DSoundBuf( ds, DSoundBuf::HW_DONT_CARE, channels, samplerate, 16, max_writeahead );

	/* Fill a buffer before we start playing, so we don't play whatever junk is
	 * in the buffer. */
	char *locked_buf;
	unsigned len;
	while( pcm->get_output_buf(&locked_buf, &len, chunksize()) )
	{
		memset( locked_buf, 0, len );
		pcm->release_output_buf(locked_buf, len);
	}

	StartDecodeThread();

	/* Start playing. */
	pcm->Play();

	MixingThread.SetName("Mixer thread");
	MixingThread.Create( MixerThread_start, this );
}
RString RageSoundDriver_ALSA9_Software::Init()
{
	RString sError = LoadALSA();
	if( sError != "" )
		return ssprintf( "Driver unusable: %s", sError.c_str() );

	g_iMaxWriteahead = safe_writeahead;
	RString sys;
	int vers;
	GetKernel( sys, vers );
	LOG->Trace( "OS: %s ver %06i", sys.c_str(), vers );
	if( sys == "Linux" && vers >= 20600 )
		g_iMaxWriteahead = g_iMaxWriteahead_linux_26;

	if( PREFSMAN->m_iSoundWriteAhead )
		g_iMaxWriteahead = PREFSMAN->m_iSoundWriteAhead;

	m_pPCM = new Alsa9Buf();
	sError = m_pPCM->Init( channels,
			g_iMaxWriteahead,
			g_iMaxWriteahead / num_chunks,
			PREFSMAN->m_iSoundPreferredSampleRate );
	if( sError != "" )
		return sError;

	m_iSampleRate = m_pPCM->GetSampleRate();
	
	StartDecodeThread();
	
	m_MixingThread.SetName( "RageSoundDriver_ALSA9_Software" );
	m_MixingThread.Create( MixerThread_start, this );

	return "";
}
RString RageSoundDriver_WaveOut::Init()
{
	m_iSampleRate = PREFSMAN->m_iSoundPreferredSampleRate;
	if( m_iSampleRate == 0 )
		m_iSampleRate = 44100;

	WAVEFORMATEX fmt;
	fmt.wFormatTag = WAVE_FORMAT_PCM;
	fmt.nChannels = channels;
	fmt.cbSize = 0;
	fmt.nSamplesPerSec = m_iSampleRate;
	fmt.wBitsPerSample = 16;
	fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample / 8;
	fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;

	MMRESULT ret = waveOutOpen( &m_hWaveOut, WAVE_MAPPER, &fmt, (DWORD_PTR) m_hSoundEvent, NULL, CALLBACK_EVENT );
	if( ret != MMSYSERR_NOERROR )
		return wo_ssprintf( ret, "waveOutOpen failed" );

	ZERO( m_aBuffers );
	for(int b = 0; b < num_chunks; ++b)
	{
		m_aBuffers[b].dwBufferLength = chunksize;
		m_aBuffers[b].lpData = new char[chunksize];
		ret = waveOutPrepareHeader( m_hWaveOut, &m_aBuffers[b], sizeof(m_aBuffers[b]) );
		if( ret != MMSYSERR_NOERROR )
			return wo_ssprintf( ret, "waveOutPrepareHeader failed" );
		m_aBuffers[b].dwFlags |= WHDR_DONE;
	}

	LOG->Info( "WaveOut software mixing at %i hz", m_iSampleRate );

	/* We have a very large writeahead; make sure we have a large enough decode
	 * buffer to recover cleanly from underruns. */
	SetDecodeBufferSize( buffersize_frames * 3/2 );
	StartDecodeThread();

	MixingThread.SetName( "Mixer thread" );
	MixingThread.Create( MixerThread_start, this );

	return RString();
}
RageSound_ALSA9_Software::RageSound_ALSA9_Software()
{
	CString err = LoadALSA();
	if( err != "" )
		RageException::ThrowNonfatal("Driver unusable: %s", err.c_str());
try {
	shutdown = false;

	max_writeahead = safe_writeahead;
	CString sys;
	int vers;
	GetKernel( sys, vers );
	LOG->Trace( "OS: %s ver %06i", sys.c_str(), vers );
	if( sys == "Linux" && vers >= 20600 )
		max_writeahead = max_writeahead_linux_26;

	if( PREFSMAN->m_iSoundWriteAhead )
		max_writeahead = PREFSMAN->m_iSoundWriteAhead;

	pcm = new Alsa9Buf( Alsa9Buf::HW_DONT_CARE, channels );

	samplerate = pcm->FindSampleRate( samplerate );
	pcm->SetSampleRate( samplerate );
	LOG->Info( "ALSA: Software mixing at %ihz", samplerate );
	
	pcm->SetWriteahead( max_writeahead );
	pcm->SetChunksize( max_writeahead / num_chunks );
	pcm->LogParams();
	
	StartDecodeThread();
	
	MixingThread.SetName( "RageSound_ALSA9_Software" );
	MixingThread.Create( MixerThread_start, this );
} catch(...) {
	UnloadALSA();
	throw;
}

}
RString RageSoundDriver_OSS::Init()
{
	fd = open("/dev/dsp", O_WRONLY|O_NONBLOCK);
	if( fd == -1 )
		return ssprintf( "RageSoundDriver_OSS: Couldn't open /dev/dsp: %s", strerror(errno) );

	RString sError = CheckOSSVersion( fd );
	if( sError != "" )
		return sError;

	int i = AFMT_S16_LE;
	if(ioctl(fd, SNDCTL_DSP_SETFMT, &i) == -1)
		return ssprintf( "RageSoundDriver_OSS: ioctl(SNDCTL_DSP_SETFMT, %i): %s", i, strerror(errno) );
	if(i != AFMT_S16_LE)
		return ssprintf( "RageSoundDriver_OSS: Wanted format %i, got %i instead", AFMT_S16_LE, i );

	i = channels;
	if(ioctl(fd, SNDCTL_DSP_CHANNELS, &i) == -1)
		return ssprintf( "RageSoundDriver_OSS: ioctl(SNDCTL_DSP_CHANNELS, %i): %s", i, strerror(errno) );
	if(i != channels)
		return ssprintf( "RageSoundDriver_OSS: Wanted %i channels, got %i instead", channels, i );
		
	i = 44100;
	if(ioctl(fd, SNDCTL_DSP_SPEED, &i) == -1 )
		return ssprintf( "RageSoundDriver_OSS: ioctl(SNDCTL_DSP_SPEED, %i): %s", i, strerror(errno) );
	samplerate = i;
	LOG->Trace("RageSoundDriver_OSS: sample rate %i", samplerate);
	i = (num_chunks << 16) + chunk_order;
	if(ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &i) == -1)
		return ssprintf( "RageSoundDriver_OSS: ioctl(SNDCTL_DSP_SETFRAGMENT, %i): %s", i, strerror(errno) );
	StartDecodeThread();
	
	MixingThread.SetName( "RageSoundDriver_OSS" );
	MixingThread.Create( MixerThread_start, this );

	return "";
}
CString RageSoundDriver_ALSA9_Software::Init()
{
	CString sError = LoadALSA();
	if( sError != "" )
		return ssprintf( "Driver unusable: %s", sError.c_str() );

	max_writeahead = safe_writeahead;
	CString sys;
	int vers;
	GetKernel( sys, vers );
	LOG->Trace( "OS: %s ver %06i", sys.c_str(), vers );
	if( sys == "Linux" && vers >= 20600 )
		max_writeahead = max_writeahead_linux_26;

	if( PREFSMAN->m_iSoundWriteAhead )
		max_writeahead = PREFSMAN->m_iSoundWriteAhead;

	pcm = new Alsa9Buf();
	sError = pcm->Init( Alsa9Buf::HW_DONT_CARE, channels );
	if( sError != "" )
		return sError;

	samplerate = pcm->FindSampleRate( samplerate );
	pcm->SetSampleRate( samplerate );
	LOG->Info( "ALSA: Software mixing at %ihz", samplerate );
	
	pcm->SetWriteahead( max_writeahead );
	pcm->SetChunksize( max_writeahead / num_chunks );
	pcm->LogParams();
	
	StartDecodeThread();
	
	MixingThread.SetName( "RageSoundDriver_ALSA9_Software" );
	MixingThread.Create( MixerThread_start, this );

	return "";
}
RageSound_OSS::RageSound_OSS()
{
	fd = open("/dev/dsp", O_WRONLY|O_NONBLOCK);
	if(fd == -1)
		RageException::ThrowNonfatal("RageSound_OSS: Couldn't open /dev/dsp: %s", strerror(errno));

	CheckOSSVersion( fd );

	shutdown = false;
	last_cursor_pos = 0;

	int i = AFMT_S16_LE;
	if(ioctl(fd, SNDCTL_DSP_SETFMT, &i) == -1)
		RageException::ThrowNonfatal("RageSound_OSS: ioctl(SNDCTL_DSP_SETFMT, %i): %s", i, strerror(errno));
	if(i != AFMT_S16_LE)
		RageException::ThrowNonfatal("RageSound_OSS: Wanted format %i, got %i instead", AFMT_S16_LE, i);

	i = channels;
	if(ioctl(fd, SNDCTL_DSP_CHANNELS, &i) == -1)
		RageException::ThrowNonfatal("RageSound_OSS: ioctl(SNDCTL_DSP_CHANNELS, %i): %s", i, strerror(errno));
	if(i != channels)
		RageException::ThrowNonfatal("RageSound_OSS: Wanted %i channels, got %i instead", channels, i);
		
	i = 44100;
	if(ioctl(fd, SOUND_PCM_WRITE_RATE, &i) == -1 )
		RageException::ThrowNonfatal("RageSound_OSS: ioctl(SOUND_PCM_WRITE_RATE, %i): %s", i, strerror(errno));
	samplerate = i;
	LOG->Trace("RageSound_OSS: sample rate %i", samplerate);
	i = (num_chunks << 16) + chunk_order;
	if(ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &i) == -1)
		RageException::ThrowNonfatal("RageSound_OSS: ioctl(SNDCTL_DSP_SETFRAGMENT, %i): %s", i, strerror(errno));

	StartDecodeThread();
	
	MixingThread.SetName( "RageSound_OSS" );
	MixingThread.Create( MixerThread_start, this );
}
/* Initialization */
std::string RageSoundDriver_PulseAudio::Init()
{
	int error = 0;

	LOG->Trace("Pulse: pa_threaded_mainloop_new()...");
	m_PulseMainLoop = pa_threaded_mainloop_new();
	if(m_PulseMainLoop == nullptr)
	{
		return "pa_threaded_mainloop_new() failed!";
	}

#ifdef PA_PROP_APPLICATION_NAME /* proplist available only since 0.9.11 */
	pa_proplist *plist = pa_proplist_new();
	pa_proplist_sets(plist, PA_PROP_APPLICATION_NAME, PACKAGE_NAME);
	pa_proplist_sets(plist, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
	pa_proplist_sets(plist, PA_PROP_MEDIA_ROLE, "game");

	LOG->Trace("Pulse: pa_context_new_with_proplist()...");

	m_PulseCtx = pa_context_new_with_proplist(
			pa_threaded_mainloop_get_api(m_PulseMainLoop),
			"StepMania", plist);
	pa_proplist_free(plist);

	if(m_PulseCtx == nullptr)
	{
		return "pa_context_new_with_proplist() failed!";
	}
#else
	LOG->Trace("Pulse: pa_context_new()...");
	m_PulseCtx = pa_context_new(
			pa_threaded_mainloop_get_api(m_PulseMainLoop),
			"Stepmania");
	if(m_PulseCtx == nullptr)
	{
		return "pa_context_new() failed!";
	}
#endif

	pa_context_set_state_callback(m_PulseCtx, StaticCtxStateCb, this);

	LOG->Trace("Pulse: pa_context_connect()...");
	error = pa_context_connect(m_PulseCtx, nullptr, (pa_context_flags_t)0, nullptr);

	if(error < 0)
	{
		return fmt::sprintf("pa_contect_connect(): %s",
			pa_strerror(pa_context_errno(m_PulseCtx)));
	}

	LOG->Trace("Pulse: pa_threaded_mainloop_start()...");
	error = pa_threaded_mainloop_start(m_PulseMainLoop);
	if(error < 0)
	{
		return fmt::sprintf("pa_threaded_mainloop_start() returned %i", error);
	}

	/* Create the decode thread, this will be needed for Mix(), that we
 	 * will use as soon as a stream is ready. */
	StartDecodeThread();

	/* Wait for the pulseaudio stream to be ready before returning.
	* An error may occur, if it appends, m_Error becomes non-nullptr. */
	m_Sem.Wait();

	if(m_Error == nullptr)
	{
		return "";
	}
	else
	{
		return m_Error;
	}
}
RString RageSoundDriver_JACK::Init()
{
	jack_status_t status;
	RString error;

	// Open JACK client and call it "StepMania" or whatever
	client = jack_client_open(PRODUCT_FAMILY, JackNoStartServer, &status);
	if (client == NULL)
		return "Couldn't connect to JACK server";

	sample_rate = jack_get_sample_rate(client);
	LOG->Trace("JACK connected at %u Hz", sample_rate);

	// Start this before callbacks
	StartDecodeThread();

	// Set callback for processing audio
	if (jack_set_process_callback(client, ProcessTrampoline, this))
	{
		error = "Couldn't set JACK process callback";
		goto out_close;
	}

	if (jack_set_sample_rate_callback(client, SampleRateTrampoline, this))
	{
		error = "Couldn't set JACK sample-rate callback";
		goto out_close;
	}

	// TODO Set a jack_on_shutdown callback as well?  Probably just stop
	// caring about sound altogether if that happens.

	// Create output ports
	port_l = jack_port_register(client, "out_l", JACK_DEFAULT_AUDIO_TYPE,
			JackPortIsOutput, 0);
	if (port_l == NULL)
	{
		error = "Couldn't create JACK port out_l";
		goto out_close;
	}

	port_r = jack_port_register(client, "out_r", JACK_DEFAULT_AUDIO_TYPE,
			JackPortIsOutput, 0);
	if (port_r == NULL)
	{
		error = "Couldn't create JACK port out_r";
		goto out_unreg_l;
	}

	// Go!
	if (jack_activate(client))
	{
		error = "Couldn't activate JACK client";
		goto out_unreg_r;
	}

	error = ConnectPorts();
	if (!error.empty())
		// Eh. Not fatal. JACK *is* running and we successfully created
		// our source ports, so it's unlkely any other driver will
		// function.
		LOG->Warn( "RageSoundDriver_JACK: Couldn't connect ports: %s", error.c_str() );

	// Success!
	LOG->Trace("JACK sound driver started successfully");
	return RString();


	// Not success!
out_unreg_r:
	jack_port_unregister(client, port_r);
out_unreg_l:
	jack_port_unregister(client, port_l);
out_close:
	jack_client_close(client);
	client = NULL;
	return error;
}
CString RageSound_CA::Init()
{
	try
	{
		AudioDeviceID dID = CAAudioHardwareSystem::GetDefaultDevice( false, false );
		mOutputDevice = new CAAudioHardwareDevice(dID);
	}
	catch (const CAException& e)
	{
		return "Couldn't create default output device.";
	}
	
	Float64 nominalSampleRate = 44100.0;
    
	try
	{
		mOutputDevice->SetNominalSampleRate(nominalSampleRate);
	}
	catch (const CAException& e)
	{
		LOG->Warn("Couldn't set the nominal sample rate.");
		nominalSampleRate = mOutputDevice->GetNominalSampleRate();
		LOG->Warn("Device's nominal sample rate is %f", nominalSampleRate);
	}
	AudioStreamID sID = mOutputDevice->GetStreamByIndex( kAudioDeviceSectionOutput, 0 );
	CAAudioHardwareStream stream( sID );

	try
	{
		mOutputDevice->AddPropertyListener(kAudioPropertyWildcardChannel,
				   kAudioPropertyWildcardSection, kAudioDeviceProcessorOverload, OverloadListener, this);
	}
	catch (const CAException& e)
	{
		LOG->Warn("Could not install the overload listener.");
	}

	// The canonical format
	// XXX should this be nominalSampleRate or not?
	Desc IOProcFormat(nominalSampleRate, kAudioFormatLinearPCM, 8, 1, 8, 2, 32,
					  kAudioFormatFlagsNativeFloatPacked);
	const Desc SMFormat(44100.0, kAudioFormatLinearPCM, kBytesPerPacket,
                        kFramesPerPacket, kBytesPerFrame, kChannelsPerFrame,
                        kBitsPerChannel, kFormatFlags);
	
	try
	{
		stream.SetCurrentIOProcFormat(IOProcFormat);
	}
	catch (const CAException& e)
	{
		LOG->Warn("Could not set the IOProc format to the canonical format.");
		stream.GetCurrentIOProcFormat(IOProcFormat);
	}
	
	if (AudioConverterNew(&SMFormat, &IOProcFormat, &mConverter))
		return "Couldn't create the audio converter";
	
	try
	{
		UInt32 bufferSize = mOutputDevice->GetIOBufferSize();
		LOG->Info("I/O Buffer size: %lu", bufferSize);
	}
	catch (const CAException& e)
	{
		LOG->Warn("Could not determine buffer size.");
	}    
    
	try
	{
		UInt32 frames = mOutputDevice->GetLatency(kAudioDeviceSectionOutput);
		if (stream.HasProperty(0, kAudioDevicePropertyLatency))
		{
			UInt32 t, size = 4;
            
			stream.GetPropertyData(0, kAudioDevicePropertyLatency, size, &t);
			frames += t;
			LOG->Info("Frames of stream latency: %lu", t);
		}
		else
			LOG->Warn("Stream reports no latency.");
		mLatency = frames / 44100.0;
		LOG->Info("Frames of latency:        %lu\n"
				  "Seconds of latency:       %f", frames, mLatency);
	}
	catch (const CAException& e)
	{
		return "Couldn't get latency.";
	}

	StartDecodeThread();
    
	try
	{
		mOutputDevice->AddIOProc(GetData, this);
		mOutputDevice->StartIOProc(GetData);
	}
	catch(const CAException& e)
	{
		return "Couldn't start the IOProc.";
	}
	return "";
}