Subversion Repositories spk

Rev

Rev 304 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
270 cycrow 1
/*
2
 Voice file Creation V1.00 Created by Cycrow (Matthew Gravestock)
3
*/
4
 
5
// Main Spk File Library Include
6
#ifdef _WIN32
7
#include <spk.h>
8
#include <StringList.h>
9
#else
10
#include "../spk/spk.h"
11
#endif
12
#include <time.h>
13
 
14
#ifdef _WIN32
15
#include <windows.h>
16
#include <direct.h>
17
#include <shlobj.h>
18
#endif
19
 
325 cycrow 20
#include <set>
21
 
270 cycrow 22
#include "sndfile.hh"
304 cycrow 23
#include "Utils/CommandLine.h"
270 cycrow 24
 
25
 
325 cycrow 26
void PrintSyntax(const Utils::WString& cmd)
27
{
28
	wprintf(L"Syntax: %s <command> [options] [arguments]\n", cmd.c_str());
29
	wprintf(L"Commands:\n");
30
	wprintf(L"\toffset [options] <offset> <file> [--out:<file>]\n");
31
	wprintf(L"\t\tEdits the stream xml file to offset the times for a voice page\n");
32
	wprintf(L"\tmerge [--out:<file>][--outxml:<file>] <file.xml> <file.wav> <mergefile.xml> <mergefile.wav>\n");
33
	wprintf(L"\t\tMerges a voice page with xml into another\n");
34
	wprintf(L"\textract [--stream:<#>] <page> <file.xml> <file.wav> <output>\n");
35
	wprintf(L"\t\tExtracts a voice page from a large file using the XML for timings\n");
36
	wprintf(L"\tcompile [--stream:<#>] <file.xml> <file.wav> <dir>\n");
37
	wprintf(L"\t\tCompiles a collection of voice files into a larger one and generates the xml file\n");
270 cycrow 38
}
39
 
40
void Pause()
41
{
42
#ifdef _DEBUG
43
	char pause;
44
	scanf ( "%s", &pause );
45
#endif
46
}
47
 
48
#define BUFFER_LEN 4096
287 cycrow 49
 
304 cycrow 50
void DoOffset(const Utils::CommandLine &cmd)
287 cycrow 51
{
304 cycrow 52
	if (cmd.argCount() < 3)
287 cycrow 53
	{
304 cycrow 54
		wprintf(L"Syntax: %s offset [options] <offset> <file> [--out:<file>]\n", cmd.cmdName().c_str());
287 cycrow 55
		wprintf(L"	<offset>\t\tOffset time to adjust by\n");
56
		wprintf(L"	<file>\t\tThe file to adjust\n");
57
		wprintf(L"\nOptions:\n");
58
		wprintf(L"  --out:<file>\t\tThe output filename, otherwise, will write to the input\n");
59
		wprintf(L"  --page:<id>\t\tThe page id to read (otherwise will do all of them)\n");
60
	}
61
	else
62
	{
304 cycrow 63
		Utils::WString offsetStr = cmd.arg(1);
64
		Utils::WString fileStr = cmd.arg(2);
65
		Utils::WString outFileStr = cmd.hasSwitch(L"out") ? cmd.switchData(L"out") : fileStr;
66
		unsigned int pageID = cmd.hasSwitch(L"page") ? cmd.switchData(L"page").toInt() : 0;
287 cycrow 67
		long offset = offsetStr.toLong();
304 cycrow 68
		bool doIgnore = cmd.hasSwitch(L"ignore");
287 cycrow 69
 
70
		CFileIO f;
71
		if (f.open(fileStr))
72
		{
73
			bool inPage = false;
289 cycrow 74
			bool ignore = false;
287 cycrow 75
			Utils::WStringList list;
76
			Utils::WStringList outList;
77
			if (f.readLines(list))
78
			{
79
				for (auto itr = list.begin(); itr != list.end(); itr++)
80
				{
81
					Utils::WString line = (*itr)->str;
82
					if (line.contains(L"<page id=\""))
83
					{
84
						long id = line.between(L"<page id=\"", L"\"").toLong();
85
						if (pageID == 0 || pageID == id)
86
							inPage = true;
289 cycrow 87
						else
88
							ignore = true;
287 cycrow 89
					}
90
					else if (line.contains(L"</page>"))
91
						inPage = false;
92
					else if (inPage)
93
					{
94
						long pos = line.findPos(L"s=\"");
95
						if (pos != -1)
96
						{
97
							Utils::WString s = line.substr(pos + 3);
98
							s = s.token(L"\"", 1);
289 cycrow 99
							long long time = s.toLong64();
100
							long long toTime = time + offset;
287 cycrow 101
							line = line.findReplace(time, toTime);
102
						}
103
					}
289 cycrow 104
 
105
					if(!ignore || !doIgnore)
106
						outList.pushBack(line);
107
 
108
					if (line.contains(L"</page>"))
109
						ignore = false;
287 cycrow 110
				}
111
				CFileIO outFile(outFileStr);
112
				outFile.writeFile(&outList);
113
				outFile.close();
114
			}
115
		}
116
		else
117
			wprintf(L"Error: Unable to open file, %s\n", fileStr.c_str());
118
	}
119
 
120
}
121
 
325 cycrow 122
void ExtractPage(int page, int stream, const Utils::WStringList& lines, Utils::WStringList& outLines)
270 cycrow 123
{
325 cycrow 124
	int inPage = 0;
125
	size_t lowestPos = 0;
126
	for (auto itr = lines.begin(); itr != lines.end(); itr++)
127
	{
128
		Utils::WString line = (*itr)->str;
129
		if (inPage)
130
		{
131
			if (line.contains(L"</page>"))
132
			{
133
				inPage = 0;// close the page
134
				return;
135
			}
136
			else if (line.contains(L"<t id="))
137
				outLines.pushBack(line);
138
		}
139
		else if (line.contains(L"<page id=\""))
140
		{
141
			long strm = line.between(L" stream=\"", L"\"").toLong();
142
			if (!stream || strm == stream)
143
			{
144
				long id = line.between(L"<page id=\"", L"\"").toLong();
145
				if (page == id)
146
					inPage = id;
147
			}
148
		}
149
	}
150
}
151
bool ExtractPageTimes(int page, int stream, std::map<int, size_t>& pageIDs, const Utils::WStringList& lines)
152
{
153
	int inPage = 0;
154
	size_t lowestPos = 0;
155
	for (auto itr = lines.begin(); itr != lines.end(); itr++)
156
	{
157
		Utils::WString line = (*itr)->str;
158
		if (inPage)
159
		{
160
			if (line.contains(L"</page>"))
161
			{
162
				if (lowestPos)
163
					pageIDs[inPage] = lowestPos;
164
				inPage = 0;// close the page
165
			}
166
			else if (line.contains(L"<t id="))
167
			{
168
				size_t s = static_cast<size_t>(line.between(L" s=\"", L"\"").toLong64());
169
				if (!lowestPos || s < lowestPos)
170
					lowestPos = s;
171
			}
172
		}
173
		else if (line.contains(L"<page id=\""))
174
		{
175
			long strm = line.between(L" stream=\"", L"\"").toLong();
176
			if (!stream || strm == stream)
177
			{
178
				long id = line.between(L"<page id=\"", L"\"").toLong();
179
				if (page == 0 || page == id)
180
					inPage = id;
181
			}
182
		}
183
	}
270 cycrow 184
 
325 cycrow 185
	return true;
186
}
270 cycrow 187
 
325 cycrow 188
void MergeXML(int id, int stream, long long offset, const Utils::WStringList &in, Utils::WStringList &out)
189
{
190
	Utils::WStringList lines;
191
	ExtractPage(id, 0, in, lines);
192
	for (auto itr2 = lines.begin(); itr2 != lines.end(); itr2++)
193
	{
194
		Utils::WString line2 = (*itr2)->str;
195
		long long i = line2.between(L" id=\"", L"\"").toLong64();
196
		long long s = line2.between(L" s=\"", L"\"").toLong64() + offset;
197
		long long l = line2.between(L" l=\"", L"\"").toLong64();
198
		out.pushBack(Utils::WString(L"\t<t id=\"") + Utils::WString(i) + L"\" s=\"" + Utils::WString(s) + L"\" l=\"" + Utils::WString(l) + "\"/>");
199
	}
200
}
201
 
202
void Merge(const Utils::CommandLine& cmd)
203
{
204
	if (cmd.argCount() < 5)
205
	{
206
		wprintf(L"Syntax: %s merge [--out:<file>][--outxml:<file>] <file.xml> <file.wav> <mergefile.xml> <mergefile.wav>\n", cmd.cmdName().c_str());
207
		wprintf(L"	<file.wav>\t\tThe main wav file to merge into\n");
208
		wprintf(L"	<file.xml>\t\tThe main xml file to merge into\n");
209
		wprintf(L"	<mergefile.wav>\t\tThe wav file to merge\n");
210
		wprintf(L"	<mergefile.xml>\t\tThe xml file to merge\n");
211
		wprintf(L"\nOptions:\n");
212
		wprintf(L"	--out:<file>\t\tOptional output wav file, otherwise will write over the input\n");
213
		wprintf(L"	--outxml:<file>\t\tOptional output xml file, otherwise will write over the input\n");
214
		wprintf(L"	--stream:<#>\t\tThe stream id to write too\n");
215
		wprintf(L"	--page:<#>\t\tThe page id to merge\n");
216
	}
287 cycrow 217
	else
218
	{
325 cycrow 219
		Utils::WString xmlfile = cmd.fullFilename(cmd.arg(1));
220
		Utils::WString wavfile = cmd.fullFilename(cmd.arg(2));
221
		Utils::WString mergexmlfile = cmd.fullFilename(cmd.arg(3));
222
		Utils::WString mergewavfile = cmd.fullFilename(cmd.arg(4));
223
		Utils::WString outwavefile = cmd.hasSwitch(L"out") ? cmd.fullFilename(cmd.switchData(L"out")) : wavfile;
224
		int stream = cmd.hasSwitch(L"stream") ? cmd.switchData(L"stream").toInt() : 3;
225
		int page = cmd.hasSwitch(L"page") ? cmd.switchData(L"page").toInt() : 0;
270 cycrow 226
 
325 cycrow 227
		if (!CFileIO(wavfile).exists())
228
		{
229
			wprintf(L"Error: Unable to open wav file, %s\n", wavfile.c_str());
230
			return;
231
		}
232
		if (!CFileIO(mergewavfile).exists())
233
		{
234
			wprintf(L"Error: Unable to open wav file, %s\n", mergewavfile.c_str());
235
			return;
236
		}
237
		CFileIO xml(xmlfile);
238
		if (!xml.exists())
239
		{
240
			wprintf(L"Error: Unable to open xml file, %s\n", xmlfile.c_str());
241
			return;
242
		}
270 cycrow 243
 
325 cycrow 244
		Utils::WStringList xmlLines;
245
		if (!xml.readLines(xmlLines))
270 cycrow 246
		{
325 cycrow 247
			wprintf(L"Error: Unable to read xml file, %s\n", xmlfile.c_str());
248
			return;
249
		}
270 cycrow 250
 
325 cycrow 251
		CFileIO mergexml(mergexmlfile);
252
		if (!mergexml.exists())
253
		{
254
			wprintf(L"Error: Unable to open xml file, %s\n", mergexmlfile.c_str());
255
			return;
256
		}
270 cycrow 257
 
325 cycrow 258
		Utils::WStringList mergeXmlLines;
259
		if (!mergexml.readLines(mergeXmlLines))
260
		{
261
			wprintf(L"Error: Unable to read xml file, %s\n", mergexmlfile.c_str());
262
			return;
263
		}
270 cycrow 264
 
325 cycrow 265
		bool overwrite = (wavfile.Compare(outwavefile));
266
 
267
		int sampleRate = 44100;
268
		{
269
			SF_INFO inInfo;
270
			SNDFILE* inputFile = sf_open(wavfile.toFileUTF8().c_str(), SFM_READ, &inInfo);
271
			if (inputFile)
270 cycrow 272
			{
325 cycrow 273
				sampleRate = inInfo.samplerate;
274
				sf_close(inputFile);
275
			}
276
		}
287 cycrow 277
 
325 cycrow 278
		SF_INFO outInfo;
279
		outInfo.channels = 1;
280
		outInfo.samplerate = sampleRate;
281
		outInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
282
		SNDFILE* outputFile = sf_open(outwavefile.toStdString().c_str(), overwrite ? SFM_RDWR : SFM_WRITE, &outInfo);
283
		if (outputFile)
284
		{
285
			double buffer[BUFFER_LEN];
286
			size_t currentPos = 0;
287 cycrow 287
 
325 cycrow 288
			// seek to end of file
289
			int check = sf_format_check(&outInfo);
290
			sf_count_t seek = sf_seek(outputFile, 0, SEEK_END);
287 cycrow 291
 
325 cycrow 292
			// if output file is different, then we need to copy the data
293
			if (!overwrite)
294
			{
295
				SF_INFO inInfo;
296
				SNDFILE* inputFile = sf_open(wavfile.toFileUTF8().c_str(), SFM_READ, &inInfo);
297
				if (inputFile)
270 cycrow 298
				{
325 cycrow 299
					double duration = static_cast<double>(inInfo.frames) / static_cast<double>(inInfo.samplerate);
300
					size_t len = static_cast<size_t>((duration * 1000.0) + 0.5);
301
					sf_count_t readcount;
302
					while ((readcount = sf_read_double(inputFile, buffer, BUFFER_LEN)) > 0)
303
						sf_write_double(outputFile, buffer, readcount);
304
					sf_close(inputFile);
305
					currentPos += len;
306
				}
307
			}
308
			else
309
				currentPos += static_cast<size_t>((static_cast<double>(outInfo.frames) / static_cast<double>(outInfo.samplerate) * 1000.0) + 0.5);
310
 
311
			size_t startPos = currentPos;
312
 
313
			// now we need to merge the wav file
314
			{
315
				SF_INFO inInfo;
316
				SNDFILE* inputFile = sf_open(mergewavfile.toFileUTF8().c_str(), SFM_READ, &inInfo);
317
				if (inputFile)
318
				{
319
					double duration = static_cast<double>(inInfo.frames) / static_cast<double>(inInfo.samplerate);
320
					size_t len = static_cast<size_t>((duration * 1000.0) + 0.5);
321
					sf_count_t readcount;
322
					while ((readcount = sf_read_double(inputFile, buffer, BUFFER_LEN)) > 0)
323
						sf_write_double(outputFile, buffer, readcount);
324
					sf_close(inputFile);
325
					currentPos += len;
326
				}
327
			}
328
			sf_close(outputFile);
329
 
330
			// finally, we need to generate the new xml file
331
			Utils::WString outxmlfile = cmd.hasSwitch(L"outxml") ? cmd.fullFilename(cmd.switchData(L"outxml")) : xmlfile;
332
			Utils::WStringList outXmlLines;
333
 
334
			// extract the pages we need to merge
335
			std::map<int, size_t> pageIDs;
336
			ExtractPageTimes(page, 0, pageIDs, mergeXmlLines);
337
 
338
			// write the new xml file
339
			for (auto itr = xmlLines.begin(); itr != xmlLines.end(); itr++)
340
			{
341
				Utils::WString line = (*itr)->str;
342
				if (line.contains(L"<page id=\""))
343
				{
344
					long id = line.between(L"<page id=\"", L"\"").toLong();
345
					auto findItr = pageIDs.find(id);
346
					if (findItr != pageIDs.end())
270 cycrow 347
					{
325 cycrow 348
						long strm = line.between(L"stream=\"", L"\"").toLong();
349
						if (strm == stream)
287 cycrow 350
						{
325 cycrow 351
							// add the page line
352
							outXmlLines.pushBack(line);
270 cycrow 353
 
325 cycrow 354
							// add all the new lines
355
							MergeXML(id, stream, static_cast<long long>(startPos) - static_cast<long long>(findItr->second), mergeXmlLines, outXmlLines);
356
							pageIDs.erase(findItr);
357
							continue;
358
						}
359
					}
360
				}
361
				else if (line.contains(L"</language>"))
362
				{
363
					// now we need to add the new lines
364
					for (auto itr = pageIDs.begin(); itr != pageIDs.end(); itr++)
365
					{
366
						// add the page line
367
						Utils::WString line = Utils::WString(L"<page id=\"") + (long)itr->first + L"\" stream=\"" + (long)stream + L"\">";
368
						outXmlLines.pushBack(line);
369
						// add all the new lines
370
						MergeXML(itr->first, stream, static_cast<long long>(startPos) - static_cast<long long>(itr->second), mergeXmlLines, outXmlLines);
371
						outXmlLines.pushBack(L"</page>");
372
					}
373
				}
270 cycrow 374
 
325 cycrow 375
				// add the line
376
				outXmlLines.pushBack(line);
377
			}
378
 
379
			CFileIO outFile(outxmlfile);
380
			bool write = outFile.writeFile(&outXmlLines);
381
 
382
			if (!cmd.hasSwitch(L"quiet"))
383
			{
384
				if(write)
385
					wprintf(L"Output XML file, %s, created\n", outxmlfile.c_str());
386
				else
387
					wprintf(L"Error: Unable to write XML file, %s\n", outxmlfile.c_str());
388
			}
389
			if (write && cmd.hasSwitch(L"showresult"))
390
				wprintf(L"Merged %s into %s at %lldms for page %d\n", CFileIO(mergewavfile).filename().c_str(), CFileIO(outwavefile).filename().c_str(), startPos, page);
391
		}
392
	}
393
}
394
 
395
void Extract(const Utils::CommandLine& cmd)
396
{
397
	if (cmd.argCount() < 5)
398
	{
399
		wprintf(L"Syntax: %s extract [--stream:<#>] <page> <file.xml> <file.wav> <output>\n", cmd.cmdName().c_str());
400
		wprintf(L"	<page>\t\tThe page id to extract\n");
401
		wprintf(L"	<file.wav>\t\tThe main wav file to extract from\n");
402
		wprintf(L"	<file.xml>\t\tThe main xml file to read timings from\n");
403
		wprintf(L"	<output>\t\tThe output wav file to create\n");
404
		wprintf(L"\nOptions:\n");
405
		wprintf(L"	--stream:<#>\t\tThe stream id to write too\n");
406
		return;
407
	}
408
 
409
	bool quiet = cmd.hasSwitch(L"quiet");
410
	bool verbose = cmd.hasSwitch(L"verbose");
411
 
412
	Utils::WString xmlfile = cmd.fullFilename(cmd.arg(2));
413
	Utils::WString wavfile = cmd.fullFilename(cmd.arg(3));
414
	Utils::WString outputfile = cmd.fullFilename(cmd.arg(4));
415
	int stream = cmd.hasSwitch(L"stream") ? cmd.switchData(L"stream").toInt() : 0;
416
	int page = cmd.arg(1).toInt();
417
 
418
	if (page <= 0)
419
	{
420
		wprintf(L"Error: Invalid page id, %s\n", cmd.arg(1).c_str());
421
		return;
422
	}
423
	if (!CFileIO(wavfile).exists())
424
	{
425
		wprintf(L"Error: Unable to open wav file, %s\n", wavfile.c_str());
426
		return;
427
	}
428
	CFileIO xml(xmlfile);
429
	if (!xml.exists())
430
	{
431
		wprintf(L"Error: Unable to open xml file, %s\n", xmlfile.c_str());
432
		return;
433
	}
434
 
435
	Utils::WStringList xmlLines;
436
	if (!xml.readLines(xmlLines))
437
	{
438
		wprintf(L"Error: Unable to read xml file, %s\n", xmlfile.c_str());
439
		return;
440
	}
441
 
442
	// find the timings for the page
443
	Utils::WStringList outLines;
444
	ExtractPage(page, stream, xmlLines, outLines);
445
 
446
	if (outLines.size() == 0)
447
	{
448
		wprintf(L"Error: Unable to find page id, %d\n", page);
449
		return;
450
	}
451
 
452
	long long startPos = outLines.front().between(L" s=\"", L"\"").toLong64();
453
	long long endPos = outLines.back().between(L" s=\"", L"\"").toLong64() + outLines.back().between(L" l=\"", L"\"").toLong64();
454
	long long length = endPos - startPos;
455
 
456
	SF_INFO inInfo;
457
	SNDFILE* inputFile = sf_open(wavfile.toFileUTF8().c_str(), SFM_READ, &inInfo);
458
	if (!inputFile)
459
	{
460
		wprintf(L"Error: Unable to open wav file, %s\n", wavfile.c_str());
461
		return;
462
	}
463
	int sampleRate = inInfo.samplerate;
464
 
465
	if (verbose)
466
	{
467
		wprintf(L"Extracting page %d from %s to %s\n", page, CFileIO(wavfile).filename().c_str(), CFileIO(outputfile).filename().c_str());
468
		wprintf(L"\tStart Time: \t%lld\n", startPos);
469
		wprintf(L"\tEnd Time: \t%lld\n", endPos);
470
		wprintf(L"\tLength: \t%lld\n", length);
471
	}
472
 
473
	long long startFrame = static_cast<long long>((static_cast<double>(startPos) / 1000.0) * sampleRate);
474
	sf_count_t seek = sf_seek(inputFile, startFrame, SEEK_SET);
475
 
476
	SF_INFO outInfo;
477
	outInfo.channels = 1;
478
	outInfo.samplerate = sampleRate;
479
	outInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
480
	SNDFILE* outputFile = sf_open(outputfile.toFileUTF8().c_str(), SFM_WRITE, &outInfo);
481
	if (outputFile)
482
	{
483
		double buffer[BUFFER_LEN];
484
		long long endFrame = static_cast<long long>((static_cast<double>(endPos) / 1000.0) * sampleRate);
485
 
486
		long long readRemaining = endFrame - startFrame;
487
		while (readRemaining > 0)
488
		{
489
			long long readAmount = readRemaining > BUFFER_LEN ? BUFFER_LEN : readRemaining;
490
			sf_count_t readcount = sf_read_double(inputFile, buffer, readAmount);
491
			sf_write_double(outputFile, buffer, readcount);
492
 
493
			readRemaining -= readcount;
494
		}
495
 
496
		sf_close(outputFile);
497
	}
498
 
499
	sf_close(inputFile);
500
 
501
	if (!quiet || cmd.hasSwitch(L"showresult"))
502
		wprintf(L"Extracted %lldms from page %d to %s\n", length, page, CFileIO(outputfile).filename().c_str());
503
}
504
 
505
void Split(const Utils::CommandLine& cmd)
506
{
507
 
508
}
509
 
510
 
511
void Compile(const Utils::CommandLine& cmd)
512
{
513
	if (cmd.argCount() < 4)
514
	{
515
		wprintf(L"Syntax: %s compile [--stream:<#>] <file.xml> <file.wav> <dir>\n", cmd.cmdName().c_str());
516
		wprintf(L"\t<file.wav>\tThe main wav file to merge into\n");
517
		wprintf(L"\t<file.xml>\tThe main xml file to merge into\n");
518
		wprintf(L"\t<dir>\t\tThe directory containing voice files\n");
519
		wprintf(L"\nOptions:\n");
520
		wprintf(L"	--stream:<#>\tThe stream id to write too\n");
521
		return;
522
	}
523
 
524
	bool quiet = cmd.hasSwitch(L"quiet");
525
	bool verbose = cmd.hasSwitch(L"verbose");
526
	bool verboseresult = cmd.hasSwitch(L"verboseresult");
527
 
528
	Utils::WString xmlfile = cmd.fullFilename(cmd.arg(1));
529
	Utils::WString wavfile = cmd.fullFilename(cmd.arg(2));
530
	Utils::WString dir = cmd.fullFilename(cmd.arg(3));
531
	int stream = cmd.hasSwitch(L"stream") ? cmd.switchData(L"stream").toInt() : 3;
532
 
533
	if (!CFileIO(wavfile).exists())
534
	{
535
		wprintf(L"Error: Unable to open wav file, %s\n", wavfile.c_str());
536
		return;
537
	}
538
	CFileIO xml(xmlfile);
539
	if (!xml.exists())
540
	{
541
		wprintf(L"Error: Unable to open xml file, %s\n", xmlfile.c_str());
542
		return;
543
	}
544
 
545
	Utils::WStringList xmlLines;
546
	if (!xml.readLines(xmlLines))
547
	{
548
		wprintf(L"Error: Unable to read xml file, %s\n", xmlfile.c_str());
549
		return;
550
	}
551
 
552
 
553
	SF_INFO info;
554
	SNDFILE* mainFile = sf_open(wavfile.toFileUTF8().c_str(), SFM_RDWR, &info);
555
	if (mainFile)
556
	{
557
		Utils::WStringList addLines;
558
 
559
		// compute the current position
560
		double duration = static_cast<double>(info.frames) / static_cast<double>(info.samplerate);
561
		size_t currentPos = static_cast<size_t>((duration * 1000.0) + 0.5);
562
 
563
		// seek to end of file
564
		int check = sf_format_check(&info);
565
		sf_count_t seek = sf_seek(mainFile, 0, SEEK_END);
566
 
567
		double buffer[BUFFER_LEN];
568
 
569
		// get all files from directory
570
		CDirIO D(dir);
571
		Utils::WStringList list;
572
		if (D.dirList(list))
573
		{
574
			for (auto itr = list.begin(); itr != list.end(); itr++)
575
			{
576
				if (D.isDir((*itr)->str))
577
				{
578
					CDirIO curDir(D.dir((*itr)->str));
579
					long pageID = (*itr)->str.toInt();
580
 
581
					addLines.pushBack(Utils::WString(L"<page id=\"") + pageID + L"\" stream=\"" + (long)stream + L"\">");
582
 
583
					Utils::WStringList idList;
584
					if (curDir.dirList(idList))
585
					{
586
						for (auto itr2 = idList.begin(); itr2 != idList.end(); itr2++)
587
						{
588
							if (curDir.isFile((*itr2)->str))
270 cycrow 589
							{
325 cycrow 590
								CFileIO F(curDir.file((*itr2)->str));
591
								long id = F.baseName().toInt();
592
								size_t len = 0;
593
 
594
								SF_INFO inInfo;
595
								SNDFILE* inputFile = sf_open(F.fullFilename().toFileUTF8().c_str(), SFM_READ, &inInfo);
596
								if (inputFile)
270 cycrow 597
								{
325 cycrow 598
									if (inInfo.samplerate != info.samplerate)
599
										wprintf(L"Error: Sample rate mismatch, %d:%s (%d) != (%d)\n", pageID, F.filename().c_str(), info.samplerate, inInfo.samplerate);
600
									else
270 cycrow 601
									{
325 cycrow 602
										double duration = static_cast<double>(inInfo.frames) / static_cast<double>(inInfo.samplerate);
603
										size_t len = static_cast<size_t>((duration * 1000.0) + 0.5);
270 cycrow 604
 
325 cycrow 605
										sf_count_t readcount;
606
										while ((readcount = sf_read_double(inputFile, buffer, BUFFER_LEN)) > 0)
607
											sf_write_double(mainFile, buffer, readcount);
608
										//int* data = new int[inInfo.frames * inInfo.channels];
609
										//sf_count_t readCount = sf_readf_int(inputFile, data, inInfo.frames);
610
										//sf_count_t writeCount = sf_writef_int(outputFile, data, inInfo.frames);
270 cycrow 611
 
325 cycrow 612
										addLines.pushBack(Utils::WString(L"\t<t id=\"") + id + L"\" s=\"" + (long)currentPos + L"\" l=\"" + (long)len + "\"/>");
270 cycrow 613
 
325 cycrow 614
										if(verbose || verboseresult)
615
											wprintf(L"Writing %d:%s to %s at %lldms\n", pageID, F.filename().c_str(), CFileIO(wavfile).filename().c_str(), currentPos);
287 cycrow 616
 
325 cycrow 617
										currentPos += len;
270 cycrow 618
									}
325 cycrow 619
									sf_close(inputFile);
270 cycrow 620
								}
621
							}
622
						}
623
					}
325 cycrow 624
 
625
					addLines.pushBack(L"</page>");
270 cycrow 626
				}
325 cycrow 627
			}
270 cycrow 628
 
325 cycrow 629
			// merge the xml file
630
			Utils::WStringList outLines;
631
			for (auto itr = xmlLines.begin(); itr != xmlLines.end(); itr++)
632
			{
633
				Utils::WString line = (*itr)->str;
634
				if (line.contains(L"</language>"))
635
				{
636
					// now we need to add the new lines
637
					for (auto itr2 = addLines.begin(); itr2 != addLines.end(); itr2++)
638
						outLines.pushBack((*itr2)->str);
639
				}
270 cycrow 640
 
325 cycrow 641
				outLines.pushBack(line);
287 cycrow 642
			}
643
 
325 cycrow 644
			CFileIO xml(xmlfile);
645
			xml.writeFile(&outLines);
270 cycrow 646
		}
325 cycrow 647
 
648
		sf_close(mainFile);
649
 
650
		if (!quiet || cmd.hasSwitch(L"showresult"))
651
		{
652
			wprintf(L"\nCompiled %s into %s\n", CFileIO(dir).filename().c_str(), CFileIO(wavfile).filename().c_str());
653
			if (verbose)
654
			{
655
				wprintf(L"\tLength: \t%lldms\n", currentPos);
656
				wprintf(L"\tStream file: \t%s\n", CFileIO(xmlfile).filename().c_str());
657
			}
658
		}
270 cycrow 659
	}
660
 
325 cycrow 661
}
662
 
663
 
664
/*
665
	Main entry point to program
666
*/
667
int main ( int argc, char **argv )
668
{
669
 
670
	Utils::CommandLine cmd(argc, argv);
671
	Utils::WString command = cmd.argCount() >= 1 ? cmd.arg(0) : L"";
672
	command.toLower();
673
 
674
	// display program header to command prompt
675
	if(!cmd.hasSwitch(L"quiet"))
676
		printf("\nX3 Voice Create V1.10 (SPK Library Version %.2f) 18/05/2025 Created by Cycrow\n\n", GetLibraryVersion());
677
 
678
	if (command == L"offset")
679
		DoOffset(cmd);
680
	else if (command == L"help")
681
		PrintSyntax(cmd.cmdName());
682
	else if (command == L"merge")
683
		Merge(cmd);
684
	else if (command == L"extract")
685
		Extract(cmd);
686
	else if (command == L"split")
687
		Split(cmd);
688
	else if (command == L"compile")
689
		Compile(cmd);
690
	else
691
		PrintSyntax(cmd.cmdName());
692
 
270 cycrow 693
	return 0;
694
}