aboutsummaryrefslogtreecommitdiffstats
path: root/code/platform_linux/linux_4ed_audio.cpp
blob: 4329dd32a3c979cbbb60e05959d4f1397f5a8188 (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
#if 0
#define ___fred_function function
#undef function
#include <alsa/asoundlib.h>
#include <poll.h>
#define function ___fred_function

internal void
linux_default_mix_sources(void *ctx, f32 *mix_buffer, u32 sample_count)
{

}

internal void
linux_default_mix_destination(i16 *dst, f32 *src, u32 sample_count)
{
    u32 opl = sample_count*2;
    for(u32 i = 0; i < sample_count; i += 1){
        dst[i] = (i16)src[i];
    }
}

internal struct alsa_funcs {
#define ALSA_FN(r,n,a) r (*n) a;
#include "alsa_funcs.txt"
#undef ALSA_FN
} snd_pcm;

internal void
linux_submit_audio(snd_pcm_t* pcm, i16* samples, u32 sample_count, f32* mix_buffer)
{
    Audio_Mix_Sources_Function *audio_mix_src;
    Audio_Mix_Destination_Function *audio_mix_dst;

    pthread_mutex_lock(&linuxvars.audio_mutex);
    audio_mix_src = linuxvars.audio_src_func;
    audio_mix_dst = linuxvars.audio_dst_func;
    void* audio_ctx = linuxvars.audio_ctx;
    pthread_mutex_unlock(&linuxvars.audio_mutex);

    if(!audio_mix_src) {
        audio_mix_src = linux_default_mix_sources;
    }

    if(!audio_mix_dst) {
        audio_mix_dst = linux_default_mix_destination;
    }

    audio_mix_src(audio_ctx, mix_buffer, sample_count);
    audio_mix_dst(samples, mix_buffer, sample_count);

    int err = snd_pcm.writei(pcm, samples, sample_count);
    if(err < 0){
	    snd_pcm.recover(pcm, err, 1);
    }
}

#define chk(x) ({\
	int err = (x);\
	if(err < 0){\
		fprintf(stderr, "ALSA ERR: %s: [%d]\n", #x, err);\
	}\
})

internal void
linux_audio_main(void* _unused)
{
    const u32 SamplesPerSecond = 48000;
    const u32 SamplesPerBuffer = 16*SamplesPerSecond/1000;
    const u32 ChannelCount = 2;
    const u32 BytesPerSample = 2; // S16LE
    const u32 BufferSize = SamplesPerBuffer * BytesPerSample;
    const u32 BufferCount = 3;
    const u32 MixBufferSize = (SamplesPerBuffer * ChannelCount * sizeof(f32));
    const u32 SampleBufferSize = (SamplesPerBuffer * ChannelCount * sizeof(i16));

    void* lib = dlopen("libasound.so.2", RTLD_LOCAL | RTLD_LAZY);
    if(!lib) {
        fprintf(stderr, "failed to load libasound.so.2: %s", dlerror());\
        return;
    }

#define ALSA_FN(r,n,a)\
    *((void**)&snd_pcm.n) = (void*)dlsym(lib, stringify(snd_pcm_##n));\
    if(!snd_pcm.n){\
        fprintf(stderr, "failed to load alsa func: %s", #n);\
        return;\
    }
#include "alsa_funcs.txt"
#undef ALSA_FN

    snd_pcm_t* pcm;

	chk( snd_pcm.open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0));

	     snd_pcm_hw_params_t*                hw;
	chk( snd_pcm.hw_params_malloc          (&hw));
	chk( snd_pcm.hw_params_any             (pcm, hw));
	chk( snd_pcm.hw_params_set_access      (pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED));
	chk( snd_pcm.hw_params_set_format      (pcm, hw, SND_PCM_FORMAT_S16_LE));
	chk( snd_pcm.hw_params_set_channels    (pcm, hw, ChannelCount));
	chk( snd_pcm.hw_params_set_rate        (pcm, hw, SamplesPerSecond, 0));
	chk( snd_pcm.hw_params_set_buffer_size (pcm, hw, BufferSize * BufferCount));
	chk( snd_pcm.hw_params                 (pcm, hw));
	     snd_pcm.hw_params_free            (hw);

	int fd_count = snd_pcm.poll_descriptors_count(pcm);
	struct pollfd* fds = (struct pollfd*)calloc(fd_count, sizeof(struct pollfd));
	snd_pcm.poll_descriptors(pcm, fds, fd_count);

    for(;;) {
		int n = poll(fds, fd_count, -1);
        if(n == -1) {
            perror("poll");
            continue;
        }

        f32* MixBuffer = (f32*)calloc(1, MixBufferSize);
        i16* SampleBuffer = (i16*)calloc(1, SampleBufferSize);

        if(!MixBuffer || !SampleBuffer) {
            perror("calloc");
            continue;
        }

        linux_submit_audio(pcm, SampleBuffer, SamplesPerBuffer, MixBuffer);

        free(MixBuffer);
        free(SampleBuffer);
    }
}

#undef chk

#endif