medfall

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

linux_audio.h (4297B)


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
#ifndef _LINUX_AUDIO_H_
#define _LINUX_AUDIO_H_

#include <stdio.h>
#include <stdarg.h>
#include <dlfcn.h>

#include <alsa/asoundlib.h>

#include "intrinsics.h"
#include "log.h"

struct AudioOutputDevice {
	snd_pcm_t * pcm;
};

void audio_output_open( AudioOutputDevice * device );
void audio_output_close( AudioOutputDevice * device );
void audio_output_write( AudioOutputDevice * device, s16 * samples, size_t num_samples );

#endif // _LINUX_AUDIO_H_

#ifdef PLATFORM_AUDIO_IMPLEMENTATION

// copied from alsa headers
static int ( *_snd_lib_error_set_handler )( snd_lib_error_handler_t handler ) = NULL;
static const char * ( *_snd_strerror )( int errnum ) = NULL;
static int ( *_snd_pcm_open )( snd_pcm_t ** pcm, const char * name, snd_pcm_stream_t stream, int mode ) = NULL;
static int ( *_snd_pcm_set_params )( snd_pcm_t * pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency ) = NULL;
static snd_pcm_sframes_t ( *_snd_pcm_writen )( snd_pcm_t * pcm, void ** bufs, snd_pcm_uframes_t size ) = NULL;
static int ( *_snd_pcm_recover )( snd_pcm_t * pcm, int err, int silent ) = NULL;
static int ( *_snd_pcm_close )( snd_pcm_t * pcm ) = NULL;

static void audio_error_handler( const char * file, int line, const char * function, int err, const char * fmt, ... ) {
	printf( "error in %s at %s:%d\n", file, function, line );

	va_list args;
	va_start( args, fmt );
	vprintf( fmt, args );
	va_end( args );
	printf( "\n" );
}

void audio_output_open( AudioOutputDevice * device ) {
	// TODO: make an audio_output_init_lib function to do this + dlload
	static bool audio_inited = false;
	if( !audio_inited ) {
		void * alsa_lib = dlopen( "libasound.so", RTLD_NOW );
		if( alsa_lib == NULL ) {
			FATAL( "Couldn't open libasound.so: %s", dlerror() );
		}

		_snd_lib_error_set_handler = ( int ( * )( snd_lib_error_handler_t ) ) dlsym( alsa_lib, "snd_lib_error_set_handler" );
		_snd_strerror = ( const char * ( * )( int ) ) dlsym( alsa_lib, "snd_strerror" );
		_snd_pcm_open = ( int ( * ) ( snd_pcm_t **, const char *, snd_pcm_stream_t, int ) ) dlsym( alsa_lib, "snd_pcm_open" );
		_snd_pcm_set_params = ( int ( * ) ( snd_pcm_t *, snd_pcm_format_t, snd_pcm_access_t, unsigned int, unsigned int, int, unsigned int ) ) dlsym( alsa_lib, "snd_pcm_set_params" );
		_snd_pcm_writen = ( snd_pcm_sframes_t ( * ) ( snd_pcm_t *, void **, snd_pcm_uframes_t ) ) dlsym( alsa_lib, "snd_pcm_writen" );
		_snd_pcm_recover = ( int ( * )( snd_pcm_t *, int, int ) ) dlsym( alsa_lib, "snd_pcm_recover" );
		_snd_pcm_close = ( int ( * ) ( snd_pcm_t * ) ) dlsym( alsa_lib, "snd_pcm_close" );

		if(
		    _snd_lib_error_set_handler == NULL
		    || _snd_strerror == NULL
		    || _snd_pcm_open == NULL
		    || _snd_pcm_set_params == NULL
		    || _snd_pcm_writen == NULL
		    || _snd_pcm_recover == NULL
		    || _snd_pcm_close == NULL
		) {
			FATAL( "Couldn't load ALSA functions" );
		}

		_snd_lib_error_set_handler( audio_error_handler );

		audio_inited = true;
	}

	const int channels = 2;
	const int sample_rate = 44100;
	const int ms = 1000;
	const int latency = 10 * ms;

	int err_open = _snd_pcm_open( &device->pcm, "default", SND_PCM_STREAM_PLAYBACK, 0 );
	if( err_open != 0 ) {
		// TODO: maybe don't kill the program
		FATAL( "Couldn't open sound output: %s", _snd_strerror( err_open ) );
	}

	int err_params = _snd_pcm_set_params( device->pcm,
		SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_NONINTERLEAVED,
		channels, sample_rate, 1, latency );
	if( err_params != 0 ) {
		// TODO: maybe don't kill the program
		FATAL( "Couldn't configure sound output: %s", _snd_strerror( err_params ) );
	}
}

void audio_output_close( AudioOutputDevice * device ) {
	int err = _snd_pcm_close( device->pcm );
	if( err != 0 ) {
		// TODO: maybe don't kill the program
		FATAL( "Couldn't close sound output: %s", _snd_strerror( err ) );
	}
}

void audio_output_write( AudioOutputDevice * device, s16 * samples, size_t num_samples ) {
	for( u32 i = 0; i < num_samples; ) {
		s16 * channels[ 2 ] = { samples + i, samples + i };

		snd_pcm_sframes_t written = _snd_pcm_writen( device->pcm, ( void ** ) channels, num_samples - i );
		if( written <= 0 ) {
			_snd_pcm_recover( device->pcm, written, 1 );
		}
		else {
			i += written;
		}
	}
}

#endif