aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/openmpt123/openmpt123_pulseaudio.hpp
blob: 9650005fa934e8b95e8af20f0c9688f24f59131c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * openmpt123_pulseaudio.hpp
 * -------------------------
 * Purpose: libopenmpt command line player
 * Notes  : (currently none)
 * Authors: OpenMPT Devs
 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
 */

#ifndef OPENMPT123_PULSEAUDIO_HPP
#define OPENMPT123_PULSEAUDIO_HPP

#include "openmpt123_config.hpp"
#include "openmpt123.hpp"

#if defined(MPT_WITH_PULSEAUDIO)

#include <pulse/pulseaudio.h>
#include <pulse/simple.h>

namespace openmpt123 {

struct pulseaudio_exception : public exception {
	static std::string error_to_string( int error ) {
		try {
			if ( error == 0 ) {
				return std::string();
			}
			std::ostringstream e;
			const char * str = pa_strerror( error );
			if ( !str ) {
				e << "error=" << error;
				return e.str();
			}
			if ( std::strlen(str) == 0 ) {
				e << "error=" << error;
				return e.str();
			}
			e << str << " (error=" << error << ")";
			return e.str();
		} catch ( const std::bad_alloc & ) {
			return std::string();
		}
	}
	pulseaudio_exception( int error ) : exception( error_to_string( error ) ) { }
};

class pulseaudio_stream_raii : public write_buffers_interface {
private:
	pa_simple * stream;
	std::size_t channels;
	std::size_t sampleSize;
	std::vector<float> sampleBufFloat;
	std::vector<std::int16_t> sampleBufInt;
public:
	pulseaudio_stream_raii( commandlineflags & flags, std::ostream & /* log */ )
		: stream(NULL)
		, channels(flags.channels)
		, sampleSize(flags.use_float ? sizeof( float ) : sizeof( std::int16_t ))
	{
		int error = 0;
		pa_sample_spec ss;
		std::memset( &ss, 0, sizeof( pa_sample_spec ) );
		ss.format = ( flags.use_float ? PA_SAMPLE_FLOAT32 : PA_SAMPLE_S16NE );
		ss.rate = flags.samplerate;
		ss.channels = flags.channels;
		pa_buffer_attr ba;
		std::memset( &ba, 0, sizeof( pa_buffer_attr ) );
		bool use_ba = false;
		if ( flags.buffer != default_high && flags.buffer != default_low ) {
			use_ba = true;
			ba.maxlength = channels * sampleSize * ( flags.buffer * flags.samplerate / 1000 );
		} else {
			ba.maxlength = static_cast<std::uint32_t>(-1);
		}
		if ( flags.period != default_high && flags.period != default_low ) {
			use_ba = true;
			ba.minreq = channels * sampleSize * ( flags.period * flags.samplerate / 1000 );
			if ( ba.maxlength != static_cast<std::uint32_t>(-1) ) {
				ba.tlength = ba.maxlength - ba.minreq;
				ba.prebuf = ba.tlength;
			} else {
				ba.tlength = static_cast<std::uint32_t>(-1);
				ba.prebuf = static_cast<std::uint32_t>(-1);
			}
		} else {
			ba.minreq = static_cast<std::uint32_t>(-1);
			ba.tlength = static_cast<std::uint32_t>(-1);
			ba.prebuf = static_cast<std::uint32_t>(-1);
		}
		ba.fragsize = 0;
		flags.apply_default_buffer_sizes();
		sampleBufFloat.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) );
		sampleBufInt.resize( channels * ( flags.ui_redraw_interval * flags.samplerate / 1000 ) );
		stream = pa_simple_new( NULL, "openmpt123", PA_STREAM_PLAYBACK, NULL, "openmpt123", &ss, NULL, ( use_ba ? &ba : NULL ), &error );
		if ( !stream ) {
			throw pulseaudio_exception( error );
		}
	}
	~pulseaudio_stream_raii() {
		int error = 0;
		if ( stream ) {
			error = 0;
			if ( pa_simple_drain( stream, &error ) < 0 ) {
				// throw pulseaudio_exception( error );
			}
			pa_simple_free( stream );
			stream = NULL;
		}
	}
private:
	template<typename Tsample>
	void write_frames( const Tsample * buffer, std::size_t frames ) {
		int error = 0;
		if ( pa_simple_write( stream, buffer, frames * channels * sampleSize, &error ) < 0 ) {
			throw pulseaudio_exception( error );
		}
	}
public:
	void write( const std::vector<float*> buffers, std::size_t frames ) override {
		sampleBufFloat.clear();
		for ( std::size_t frame = 0; frame < frames; ++frame ) {
			for ( std::size_t channel = 0; channel < channels; ++channel ) {
				sampleBufFloat.push_back( buffers[channel][frame] );
			}
		}
		write_frames( sampleBufFloat.data(), frames );
	}
	void write( const std::vector<std::int16_t*> buffers, std::size_t frames ) override {
		sampleBufInt.clear();
		for ( std::size_t frame = 0; frame < frames; ++frame ) {
			for ( std::size_t channel = 0; channel < channels; ++channel ) {
				sampleBufInt.push_back( buffers[channel][frame] );
			}
		}
		write_frames( sampleBufInt.data(), frames );
	}
	bool unpause() override {
		return true;
	}
	bool pause() override {
		int error = 0;
		error = 0;
		if ( pa_simple_drain( stream, &error ) < 0 ) {
			throw pulseaudio_exception( error );
		}
		return true;
	}
	bool sleep( int ms ) override {
		pa_msleep( ms );
		return true;
	}
};

static std::string show_pulseaudio_devices( std::ostream & /* log */ ) {
	std::ostringstream devices;
	devices << " pulseaudio:" << std::endl;
	devices << "    " << "0" << ": Default Device" << std::endl;
	return devices.str();
}

} // namespace openmpt123

#endif // MPT_WITH_PULSEAUDIO

#endif // OPENMPT123_PULSEAUDIO_HPP