forked from chmorgan/esp-audio-player
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathaudio_wav.cpp
More file actions
158 lines (131 loc) · 5.45 KB
/
Copy pathaudio_wav.cpp
File metadata and controls
158 lines (131 loc) · 5.45 KB
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
#include <string.h>
#include <stdio.h>
#include "audio_wav.h"
#include "audio_stream_io.h"
static const char *TAG = "wav";
/**
* @param fp
* @param pInstance - Values can be considered valid if true is returned
* @return true if file is a wav file
*/
bool is_wav(FILE *fp, wav_instance *pInstance) {
fseek(fp, 0, SEEK_SET);
size_t bytes_read = fread(&pInstance->header, 1, sizeof(wav_header_t), fp);
if(bytes_read != sizeof(wav_header_t)) {
return false;
}
wav_header_t *wav_head = &pInstance->header;
if((NULL == strstr(reinterpret_cast<char *>(wav_head->ChunkID), "RIFF")) ||
(NULL == strstr(reinterpret_cast<char*>(wav_head->Format), "WAVE"))
)
{
return false;
}
// decode chunks until we find the 'data' one
wav_subchunk_header_t subchunk;
while(true) {
bytes_read = fread(&subchunk, 1, sizeof(wav_subchunk_header_t), fp);
if(bytes_read != sizeof(wav_subchunk_header_t)) {
return false;
}
if(memcmp(subchunk.SubchunkID, "data", 4) == 0)
{
break;
} else {
// advance beyond this subchunk, it could be a 'LIST' chunk with file info or some other unhandled subchunk
fseek(fp, subchunk.SubchunkSize, SEEK_CUR);
}
}
LOGI_2("sample_rate=%d, channels=%d, bps=%d",
wav_head->SampleRate,
wav_head->NumChannels,
wav_head->BitsPerSample);
return true;
}
/**
* @return true if data remains, false on error or end of file
*/
DECODE_STATUS decode_wav(FILE *fp, decode_data *pData, wav_instance *pInstance) {
// read an even multiple of frames that can fit into output_samples buffer, otherwise
// we would have to manage what happens with partial frames in the output buffer
size_t bytes_per_frame = (pInstance->header.BitsPerSample / BITS_PER_BYTE) * pInstance->header.NumChannels;
size_t frames_to_read = pData->samples_capacity / bytes_per_frame;
size_t bytes_to_read = frames_to_read * bytes_per_frame;
size_t bytes_read = fread(pData->samples, 1, bytes_to_read, fp);
pData->fmt.channels = pInstance->header.NumChannels;
pData->fmt.bits_per_sample = pInstance->header.BitsPerSample;
pData->fmt.sample_rate = pInstance->header.SampleRate;
if(bytes_read != 0)
{
pData->frame_count = (bytes_read / (pInstance->header.BitsPerSample / BITS_PER_BYTE)) / pInstance->header.NumChannels;
} else {
pData->frame_count = 0;
}
LOGI_2("bytes_per_frame %d, bytes_to_read %d, bytes_read %d, frame_count %d",
bytes_per_frame, bytes_to_read, bytes_read,
pData->frame_count);
return (bytes_read == 0) ? DECODE_STATUS_DONE : DECODE_STATUS_CONTINUE;
}
/**
* @param io
* @param pInstance - Values can be considered valid if true is returned
* @return true if stream is a wav file
*/
bool is_wav_io(audio_stream_io_handle_t io, wav_instance *pInstance) {
audio_stream_io_seek(io, 0, AUDIO_STREAM_SEEK_SET);
size_t bytes_read = audio_stream_io_read(io, &pInstance->header, sizeof(wav_header_t));
if(bytes_read != sizeof(wav_header_t)) {
return false;
}
wav_header_t *wav_head = &pInstance->header;
if((NULL == strstr(reinterpret_cast<char *>(wav_head->ChunkID), "RIFF")) ||
(NULL == strstr(reinterpret_cast<char*>(wav_head->Format), "WAVE"))
)
{
return false;
}
// decode chunks until we find the 'data' one
wav_subchunk_header_t subchunk;
while(true) {
bytes_read = audio_stream_io_read(io, &subchunk, sizeof(wav_subchunk_header_t));
if(bytes_read != sizeof(wav_subchunk_header_t)) {
return false;
}
if(memcmp(subchunk.SubchunkID, "data", 4) == 0)
{
break;
} else {
// advance beyond this subchunk, it could be a 'LIST' chunk with file info or some other unhandled subchunk
audio_stream_io_seek(io, subchunk.SubchunkSize, AUDIO_STREAM_SEEK_CUR);
}
}
LOGI_2("sample_rate=%d, channels=%d, bps=%d",
wav_head->SampleRate,
wav_head->NumChannels,
wav_head->BitsPerSample);
return true;
}
/**
* @return true if data remains, false on error or end of file
*/
DECODE_STATUS decode_wav_io(audio_stream_io_handle_t io, decode_data *pData, wav_instance *pInstance) {
// read an even multiple of frames that can fit into output_samples buffer, otherwise
// we would have to manage what happens with partial frames in the output buffer
size_t bytes_per_frame = (pInstance->header.BitsPerSample / BITS_PER_BYTE) * pInstance->header.NumChannels;
size_t frames_to_read = pData->samples_capacity / bytes_per_frame;
size_t bytes_to_read = frames_to_read * bytes_per_frame;
size_t bytes_read = audio_stream_io_read(io, pData->samples, bytes_to_read);
pData->fmt.channels = pInstance->header.NumChannels;
pData->fmt.bits_per_sample = pInstance->header.BitsPerSample;
pData->fmt.sample_rate = pInstance->header.SampleRate;
if(bytes_read != 0)
{
pData->frame_count = (bytes_read / (pInstance->header.BitsPerSample / BITS_PER_BYTE)) / pInstance->header.NumChannels;
} else {
pData->frame_count = 0;
}
LOGI_2("bytes_per_frame %d, bytes_to_read %d, bytes_read %d, frame_count %d",
(int)bytes_per_frame, (int)bytes_to_read, (int)bytes_read,
(int)pData->frame_count);
return (bytes_read == 0) ? DECODE_STATUS_DONE : DECODE_STATUS_CONTINUE;
}