-
Notifications
You must be signed in to change notification settings - Fork 1
/
samh.c
226 lines (198 loc) · 7.5 KB
/
samh.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
/* lopwav. the idea is to lop off either the start or the end of the file. All from the command line */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#define MXINTV 0x7FFFFFFF /* max int value */
#define ARBSZ 128
/* hardcoded number of samples */
#define NCAPS 100 /* strictly, "captures", because a sample is a time sample really. A capture is a sample from one channel */
#define SAMPF 440 /* in practice, we'll be clueless about the frequency, we would have to chose a nice one using ear */
typedef struct /* time point, tpt */
{
int m, s, h;
} tpt;
typedef struct
{
char id[4]; // should always contain "RIFF"
int glen; // general length: total file length minus 8, could because the types so far seen (id and glen itself) are actually 8 bytes
char fstr[8]; // format string should be "WAVEfmt ": actually two 4chartypes here.
int fmtnum; // format number 16 for PCM format
short pcmnum; // PCM number 1 for PCM format
short nchans; // num channels
int sampfq; // sampling frequency: CD quality is 44100, 48000 is also common.
int byps; // BYtes_Per_Second (aka, BlockAlign) = numchannels* bytes per sample*samplerate. stereo shorts at 44100 . should be 176k.
short bypc; // BYtes_Per_Capture, bipsamp/8 most probably. A capture is the same as a sample if mono, but half a sample if stereo. bypc usually 2, a short.
short bipsamp; // bits_per_sample: CD quality is 16.
char datastr[4]; // should always contain "data"
int byid; // BYtes_In_Data;
} wh_t; /* wav header type */
tpt *s2tp(char *str)
{
tpt *p=malloc(sizeof(tpt));
char *tc, staminchar[5]={0};
if( (tc=strchr(str, ':')) == NULL) {
printf("There are no minutes \n");
p->m=0;
tc=str;
} else {
strncpy(staminchar, str, (int)(tc-str));
p->m=atoi(staminchar);
tc++;
}
char *ttc, stasecchar[5]={0};
if( (ttc=strchr(tc, '.')) == NULL) {
printf("There are no seconds\n");
p->s=0;
ttc=tc;
} else {
strncpy(stasecchar, tc, (int)(ttc-tc));
p->s=atoi(stasecchar);
ttc++;
}
char stahunschar[5]={0};
strcpy(stahunschar, ttc);
p->h=atoi(stahunschar);
if((strlen(stahunschar))==1)
p->h*=10;
return p;
}
wh_t *hdr4chunk(int sfre, char nucha, int certainsz) /* a header for a file chunk of certain siez */
{
wh_t *wh=malloc(sizeof(wh_t));
strncpy(wh->id, "RIFF", 4);
strncpy(wh->fstr, "WAVEfmt ", 8);
strncpy(wh->datastr, "data", 4);
wh->fmtnum=16;
wh->pcmnum=1;
wh->nchans=nucha; /* fed in to function */
wh->sampfq=sfre; /* fed in to function */
wh->glen=certainsz-8;
wh->bipsamp=16;
wh->bypc=wh->bipsamp/8;
wh->byps=wh->nchans*wh->sampfq*wh->bypc;
wh->byid=wh->glen-36;
return wh;
}
int hdrchk(wh_t *inhdr)
{
/* OK .. we test what sort of header we have */
if ( inhdr->id[0] != 'R'
|| inhdr->id[1] != 'I'
|| inhdr->id[2] != 'F'
|| inhdr->id[3] != 'F' ) {
printf("ERROR: RIFF string problem\n");
return 1;
}
if ( inhdr->fstr[0] != 'W'
|| inhdr->fstr[1] != 'A'
|| inhdr->fstr[2] != 'V'
|| inhdr->fstr[3] != 'E'
|| inhdr->fstr[4] != 'f'
|| inhdr->fstr[5] != 'm'
|| inhdr->fstr[6] != 't'
|| inhdr->fstr[7] != ' ' ) {
printf("ERROR: WAVEfmt string problem\n");
return 1;
}
if ( inhdr->datastr[0] != 'd'
|| inhdr->datastr[1] != 'a'
|| inhdr->datastr[2] != 't'
|| inhdr->datastr[3] != 'a' ) {
printf("WARNING: header \"data\" string does not come up\n");
return 1;
}
if ( inhdr->fmtnum != 16 ) {
printf("WARNING: fmtnum is %i, while it's better off being %i\n", inhdr->fmtnum, 16);
return 1;
}
if ( inhdr->pcmnum != 1 ) {
printf("WARNING: pcmnum is %i, while it's better off being %i\n", inhdr->pcmnum, 1);
return 1;
}
printf("There substantial evidence in the non-numerical fields of this file's header to think it is a wav file\n");
printf("glen: %i\n", inhdr->glen);
printf("byid: %i\n", inhdr->byid);
printf("nchans: %d\n", inhdr->nchans);
printf("sampfq: %i\n", inhdr->sampfq);
printf("byps: %i\n", inhdr->byps);
printf("bypc, bytes by capture (count of data in shorts): %d\n", inhdr->bypc);
printf("bipsamp: %d\n", inhdr->bipsamp);
if(inhdr->glen+8-44 == inhdr->byid)
printf("Good, \"byid\" (%i) is 36 bytes smaller than \"glen\" (%i).\n", inhdr->byid, inhdr->glen);
else {
printf("WARNING: glen (%i) and byid (%i)do not show prerequisite normal relation(diff is %i).\n", inhdr->glen, inhdr->byid, inhdr->glen-inhdr->byid);
}
// printf("Duration by glen is: %f\n", (float)(inhdr->glen+8-wh_tsz)/(inhdr->nchans*inhdr->sampfq*inhdr->byps));
printf("Duration by byps is: %f\n", (float)(inhdr->glen+8-44)/inhdr->byps);
if( (inhdr->bypc == inhdr->byps/inhdr->sampfq) && (inhdr->bypc == 2) )
printf("bypc complies with being 2 and matching byps/sampfq. Data values can therefore be recorded as signed shorts.\n");
return 0;
}
char *xfw(char *inwf, char *tp, wh_t *inhdr) /* xfw: extract from wav */
{
FILE *inwavfp=fopen(inwf,"rb");
if ( inwavfp == NULL ) {
fprintf(stderr,"Can't open input file %s", inwf);
exit(EXIT_FAILURE);
}
if ( fread(inhdr, sizeof(wh_t), sizeof(char), inwavfp) < 1 ) {
printf("Can't read file header\n");
exit(EXIT_FAILURE);
}
tpt *p=s2tp(tp);
printf("stamin=%u, stasec=%u, stahuns=%u\n", p->m, p->s, p->h);
int point=inhdr->byps*(p->m*60 + p->s) + p->h*inhdr->byps/100;
printf("point to is %i inside %i which is %.1f%% in.\n", point, inhdr->byid, (point*100.)/inhdr->byid);
if(point >= inhdr->byid) {
printf("Timepoint at which to sample is over the size of the wav file. Aborting.\n");
exit(EXIT_FAILURE);
}
/* recreate based on samples taken */
inhdr->byid = inhdr->bypc*inhdr->nchans*NCAPS; /* number of bytes to extract */
inhdr->glen = inhdr->byid+36;
char *bf=malloc(inhdr->byid);
fseek(inwavfp, point, SEEK_CUR);
if ( fread(bf, inhdr->byid, sizeof(char), inwavfp) < 1 ) { /* Yup! we slurp in the baby! */
printf("Sorry, trouble putting input file into array. Overshot maybe?\n");
exit(EXIT_FAILURE);
}
fclose(inwavfp);
free(p);
return bf;
}
int main(int argc, char *argv[])
{
if(argc != 3) {
printf("Usage: samples from a wav's time point Args: 1) Name of wavfile 2) mm:ss.hh string say to where sampling should begin.\n");
exit(EXIT_FAILURE);
}
int i;
wh_t *twhdr=malloc(sizeof(wh_t));
char *bf= xfw(argv[1], argv[2], twhdr);
short *vals=malloc((twhdr->byid/2)*sizeof(short));
for(i=0;i<twhdr->byid/2;i++) {
vals[i]=bf[2*i+1]<<8;
vals[i] |=bf[2*i];
}
/* now the followng is just a sanity check, the bf values are being interpreted
* as bytes here, when really they're shorts. */
if(twhdr->nchans ==2) {
/* first channel */
for(i=0;i<twhdr->byid/2;i+=2)
printf("%d ", vals[i]);
printf("\n");
/* second channel */
for(i=1;i<twhdr->byid/2;i+=2)
printf("%d ", vals[i]);
printf("\n");
} else { /* it's in mono */
for(i=0;i<twhdr->byid/2;i++)
printf("%d ", vals[i]);
printf("\n");
}
free(twhdr);
free(vals);
free(bf);
return 0;
}