-
Notifications
You must be signed in to change notification settings - Fork 0
/
PianoPlayer.cpp
333 lines (265 loc) · 9.45 KB
/
PianoPlayer.cpp
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/* Compile with gcc -lasound -pthread threadaudio.c */
#include <alsa/asoundlib.h>
#include <pthread.h>
#include <stdio.h>
#include <fstream>
#include <iostream>
#include <semaphore.h>
using namespace std;
typedef struct WAV_HEADER{
char RIFF[4]; // RIFF Header Magic header
unsigned long ChunkSize; // RIFF Chunk Size
char WAVE[4]; // WAVE Header
char fmt[4]; // FMT header
unsigned long Subchunk1Size; // Size of the fmt chunk
unsigned short AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
unsigned short NumOfChan; // Number of channels 1=Mono 2=Sterio
unsigned long SamplesPerSec; // Sampling Frequency in Hz
unsigned long bytesPerSec; // bytes per second
unsigned short blockAlign; // 2=16-bit mono, 4=16-bit stereo
unsigned short bitsPerSample; // Number of bits per sample
char Subchunk2ID[4]; // "data" string
unsigned long Subchunk2Size; // Sampled data length
}wav_hdr;
#define PCM_DEVICE "default"
#define C1 "316898__jaz-the-man-2__do.wav"
#define D1 "316908__jaz-the-man-2__re.wav"
#define E1 "316906__jaz-the-man-2__mi.wav"
#define F1 "316904__jaz-the-man-2__fa.wav"
#define G1 "316912__jaz-the-man-2__sol.wav"
#define A1 "316902__jaz-the-man-2__la.wav"
#define B1 "316913__jaz-the-man-2__si.wav"
#define FILE_COUNT 7
//#define DEBUG
#ifdef DEBUG
#define PRINT(...) do { printf(__VA_ARGS__); } while (0)
#else
#define PRINT(...)
#endif
const char *file[FILE_COUNT] = { C1, D1, E1, F1, G1, A1, B1};
char* waveBuffer[FILE_COUNT];
int waveOffset[FILE_COUNT] = {-1, -1, -1, -1, -1, -1, -1};
int fileSize[FILE_COUNT];
unsigned char audiobuffer[0x40000];
unsigned char mixbuffer[0x40000];
pthread_mutex_t audiomutex = PTHREAD_MUTEX_INITIALIZER;
int frameBufferSize = 0;
int playSize = 0;
int audiostop = 0;
sem_t empty, full;
void *audioPlayer (void *param)
{
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
unsigned int rate, channels;
unsigned int pcm;
rate = 44100;
channels = 2;
/* Open the PCM device in playback mode */
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE,
SND_PCM_STREAM_PLAYBACK, 0) < 0)
{
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
PCM_DEVICE, snd_strerror(pcm));
return 0;
}
/* Allocate parameters object and fill it with default values*/
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm_handle, params);
/* Set parameters */
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
SND_PCM_FORMAT_S16_LE) < 0)
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0)
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0)
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
/* Write parameters */
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0)
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
/* Allocate buffer to hold single period */
snd_pcm_hw_params_get_period_size(params, &frames, 0);
frameBufferSize = frames * channels * 2 /* 2 -> sample size */;
int *audiostop = (int*)param;
while (!*audiostop) {
(void) sem_wait(&full);
int size = playSize;
int offset = 0;
while (size - offset >= frameBufferSize) {
pcm = snd_pcm_writei(pcm_handle, audiobuffer + offset, frames);
if (pcm == -EPIPE) {
printf("XRUN.\n");
snd_pcm_prepare(pcm_handle);
} else if (pcm < 0) {
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
} else {
offset += pcm;
}
}
if (size > 0)
PRINT("play %d / %d\n", offset, size);
(void) sem_post(&empty);
}
snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_handle);
}
void *mixer (void *param)
{
int *audiostop = (int*)param;
while (!*audiostop) {
int maxSize = frameBufferSize;
//int maxSize = sizeof(mixbuffer);
memset(mixbuffer, 0, maxSize);
int step = sizeof(short);
int i = 0;
for (i = 0; i < maxSize; i += step) {
int total = 0;
int count = 0;
for (int j = 0; j < FILE_COUNT; j ++)
{
if (waveOffset[j] >= 0)
{
if (waveOffset[j] >= fileSize[j])
{
waveOffset[j] = -1;
}
else
{
short value;
memcpy(&value, waveBuffer[j] + waveOffset[j], step);
total += value;
count++;
waveOffset[j] += step;
}
}
}
if (count > 0) {
short value = (short)(total / (float)count);
memcpy(mixbuffer + i, &value, step);
}
else
{
break;
}
}
playSize = i;
if (i > 0)
{
PRINT("Wave offset: ");
for (int j = 0; j < FILE_COUNT; j++)
{
PRINT("%d:%d \t", j, waveOffset[j]);
}
PRINT("\n");
}
(void) sem_wait(&empty);
memcpy(audiobuffer, mixbuffer, i);
(void) sem_post(&full);
}
}
int loadFiles() {
for (int i = 0; i < FILE_COUNT; i++)
{
ifstream is(file[i]);
is.seekg(0, ios::end);
int length = is.tellg();
printf("%d %s wave size: %d\n", i, file[i], length);
is.seekg(0, ios::beg);
wav_hdr wavHeader;
is.read ((char*)&wavHeader, sizeof(wav_hdr));
int offset = (length - wavHeader.Subchunk2Size);
int dataSize = length - offset;
printf("\t\theader size: %d\n", offset);
printf("\t\tdata size: %d\n", dataSize);
is.seekg(offset, ios::beg);
waveBuffer[i] = (char*)malloc(dataSize);
fileSize[i] = dataSize;
is.seekg(offset, ios::beg);
is.read (waveBuffer[i], dataSize);
is.close();
}
return 0;
}
pthread_t audiothread;
pthread_t mixerThread;
int initSound(void)
{
printf( "Odroid Ultrasonic sensor Piano program\n" );
printf( "creating threads\n" );
sem_init(&empty, 0, 1);
sem_init(&full, 0, 0);
pthread_attr_t tattr;
int ret;
int newprio = -20;
sched_param param;
/* initialized with default attributes */
ret = pthread_attr_init (&tattr);
/* safe to get existing scheduling param */
ret = pthread_attr_getschedparam (&tattr, ¶m);
/* set the priority; others are unchanged */
param.sched_priority = newprio;
/* setting the new scheduling param */
ret = pthread_attr_setschedparam (&tattr, ¶m);
// ALSA Player 생성.
// Audio QUEUE에 값이 들어있다면 계속 Play한다.
pthread_create(&audiothread, &tattr, audioPlayer, &audiostop);
// Mux thread
// 개별 wave 파일의 offset 정보를 가지고 있고 offset에서 frame size만큼 하나의 출력으로 만들어 Audio Queue에 넣어 play 하도록 한다.
pthread_create(&mixerThread, NULL, mixer, &audiostop);
//memset(playing, 0, sizeof(gpio));
printf( "Loading wave files\n" );
loadFiles();
printf( "Load completed\n" );
return 0;
}
void stop()
{
audiostop = 1;
pthread_join(audiothread, NULL);
}
void play(int index)
{
waveOffset[index] = 0;
}
void printWaveHeader(int index)
{
wav_hdr* wavHeader = (wav_hdr*)waveBuffer[index];
int headerSize = sizeof(wav_hdr),filelength = fileSize[index];
cout << "File is :" << filelength << " bytes." << endl;
cout << "RIFF header :" << wavHeader->RIFF[0]
<< wavHeader->RIFF[1]
<< wavHeader->RIFF[2]
<< wavHeader->RIFF[3] << endl;
cout << "WAVE header :" << wavHeader->WAVE[0]
<< wavHeader->WAVE[1]
<< wavHeader->WAVE[2]
<< wavHeader->WAVE[3]
<< endl;
cout << "FMT :" << wavHeader->fmt[0]
<< wavHeader->fmt[1]
<< wavHeader->fmt[2]
<< wavHeader->fmt[3]
<< endl;
cout << "Data size :" << wavHeader->ChunkSize << endl;
// Display the sampling Rate form the header
cout << "Sampling Rate :" << wavHeader->SamplesPerSec << endl;
cout << "Number of bits used :" << wavHeader->bitsPerSample << endl;
cout << "Number of channels :" << wavHeader->NumOfChan << endl;
cout << "Number of bytes per second :" << wavHeader->bytesPerSec << endl;
cout << "Data length :" << wavHeader->Subchunk2Size << endl;
cout << "Audio Format :" << wavHeader->AudioFormat << endl;
// Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
cout << "Block align :" << wavHeader->blockAlign << endl;
cout << "Data string :" << wavHeader->Subchunk2ID[0]
<< wavHeader->Subchunk2ID[1]
<< wavHeader->Subchunk2ID[2]
<< wavHeader->Subchunk2ID[3]
<< endl;
cout << "Header size :" << (filelength - wavHeader->ChunkSize) << endl;
cout << "Header size2 :" << (filelength - wavHeader->Subchunk2Size) << endl;
cout << endl << endl;
}