-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterface_hardware.c
228 lines (183 loc) · 6.04 KB
/
interface_hardware.c
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#include <stdio.h>
#include <stdlib.h>
#include "cbuffer.h"
#include "hybrid.h"
#include "interface_hardware.h"
#include "kodama.h"
#include "portaudio.h"
/*********** Globals ***********/
/* Has portaudio been initialized? */
static int pa_initted = 0;
/* Only one set of speaker/mic per machine, so these are global */
static PaStream *in_stream, *out_stream;
/* Let portaudio choose how many frames it wants per buffer */
int gFramesPerBuffer = paFramesPerBufferUnspecified;
extern globals_t globals;
/*********** Static prototypes ***********/
static void init_portaudio(void);
static int handle_read( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData );
static int handle_write( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData );
/*********** Static functions ***********/
static void init_portaudio(void)
{
PaError err = paNoError;
if (pa_initted)
{
/* Already initted */
return;
}
pa_initted = 1;
err = Pa_Initialize();
if (err != paNoError)
{
fprintf(stderr, "portaudio error: %s\n", Pa_GetErrorText(err));
pa_initted = 0;
}
}
void setup_hw_in(hybrid *h)
{
PaError err = paNoError;
init_portaudio();
PaStreamParameters inputParameters;
inputParameters.device = Pa_GetDefaultInputDevice();
inputParameters.channelCount = NUM_CHANNELS;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = \
Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(&in_stream, &inputParameters, NULL, globals.sample_rate,
gFramesPerBuffer,
paClipOff,
handle_read,
h);
if (err != paNoError) goto done;
Pa_StartStream(in_stream);
done:
if (err != paNoError)
{
fprintf(stderr, "portaudio error: %s\n", Pa_GetErrorText(err));
}
}
/* Set up a stream to accept data from elsewhere and write it to the speaker. I
* guess it can have a callback function too, but I'm not sure what it will be
* used for */
void setup_hw_out(hybrid *h)
{
/* TODO: when the hybrid is destroyed, we need to remove this stream
* attached to it */
PaError err = paNoError;
init_portaudio();
PaStreamParameters outputParameters;
outputParameters.device = Pa_GetDefaultOutputDevice();
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = \
Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(&out_stream, NULL, &outputParameters,
globals.sample_rate,
gFramesPerBuffer,
paClipOff,
handle_write,
h);
if (err != paNoError) goto done;
Pa_StartStream(out_stream);
done:
if (err != paNoError)
{
fprintf(stderr, "portaudio error: %s\n", Pa_GetErrorText(err));
}
}
static int handle_read( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
hybrid *h = (hybrid *)userData;
const SAMPLE *rptr = (const SAMPLE *)inputBuffer;
UNUSED(outputBuffer);
UNUSED(timeInfo);
UNUSED(statusFlags);
/* DEBUG_LOG("handle_read (hardware): %li frames requested\n", framesPerBuffer); */
size_t num_samples_needed = framesPerBuffer * NUM_CHANNELS;
SAMPLE_BLOCK *sb = sample_block_create(num_samples_needed);
SAMPLE *s = sb->s;
if( inputBuffer == NULL )
{
/* Push some silence */
while(num_samples_needed--)
{
*s++ = SAMPLE_SILENCE;
}
}
else
{
/* This may discard old data from the hybrid's tx_buf if it hasn't been
* gotten to yet. This is intentional */
while(num_samples_needed--)
{
*s++ = *rptr++;
}
}
hybrid_put_tx_samples(h, sb);
sample_block_destroy(sb);
return 0;
}
static int handle_write( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
hybrid *h = (hybrid *)userData;
SAMPLE *wptr = (SAMPLE*)outputBuffer;
/* DEBUG_LOG("handle_write (hardware): %li frames requested\n", framesPerBuffer); */
UNUSED(inputBuffer);
UNUSED(timeInfo);
UNUSED(statusFlags);
/* Currently, we're relying on the fact that if the cbuffer doesn't have
* enough frames to satisfy our request, it will return silence. */
size_t num_samples_needed = framesPerBuffer * NUM_CHANNELS;
SAMPLE_BLOCK *sb = hybrid_get_rx_samples(h, num_samples_needed);
SAMPLE *s = sb->s;
size_t copied = 0;
while (copied++ < num_samples_needed)
{
*wptr++ = *s++;
}
sample_block_destroy(sb);
/* This is the end of the line for audio data - it's been dumped to the
* hardware (outputBuffer). No further callbacks possible */
return 0;
}
/************** Public functions **************/
void list_hw_input_devices(void)
{
int i;
int defaultInput, defaultOutput;
int numDevices;
const PaDeviceInfo *deviceInfo;
init_portaudio();
defaultInput = Pa_GetDefaultInputDevice();
defaultOutput = Pa_GetDefaultOutputDevice();
numDevices = Pa_GetDeviceCount();
for(i=0; i<numDevices; i++)
{
deviceInfo = Pa_GetDeviceInfo(i);
fprintf(stderr, "%d\t%s ", i, deviceInfo->name);
if (i == defaultInput)
fprintf(stderr, "*");
if (i == defaultOutput)
fprintf(stderr, "%%");
fprintf(stderr, "\n");
}
}