«SpeechRender / SpeechBuffer» by rukano

on 11 Feb'12 13:35 in speechclassrenderbuffer

Little class I made two years ago to generate buffers out of the speech synthesis engine in Mac OS X. If there is something similar from the command line in Linux or windows, should be easy to adapt the command for those platforms. One of the first things one learn as SC beginner is "bla bla".speak and it is a lot of fun. But one of the most common problems is: one wants to use that command in SynthDefs. If you like the speech synthesizer now you can turn it into a buffer and use it as you wish.

Usage:

b = SpeechBuffer("I am Super... Collider", 3); // string, voice

b.play;

play{ PlayBuf.ar(1, b, BufRateScale.kr(b) * 0.5, loop:1)!2 }

And you have to clean up your rendering directory with:

SpeechBuffer.cleanUp // or: SpeechRender.cleanUp

If maybe someone want, this could be turned into a Quark (?) Have fun!

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
/*

Little class to render terminal voices into a temp folder (or a given path)
Also can be loaded as buffer. Subclass SpeechBuffer calls the render and loads the buffer automatically. May be adaptable to work on linux (if I knew the command used on linux for rendering from speech synthesis)


// USAGE:

b = SpeechBuffer("I am Super... Collider", 3); // string, voice
b.play;
play{ PlayBuf.ar(1, b, BufRateScale.kr(b) * 0.5, loop:1)!2 }

// WARNING!
// do this regularily until I find a better way to deal with the temp files

SpeechBuffer.cleanUp

// WARNING!
// Files have a 22050Hz sample rate!

*/

SpeechRender {
	
	classvar <>voices, <>defaultVoice;
	classvar <>tempDir, <>tempPrefix;
	
	var <cmd, <filePath;
	
	*initClass {
		voices = ();
		voices.all = [
			// 0..4			
			'Agnes', 'Kathy', 'Princess', 'Vicki', 'Victoria',
			// 5..9			
			'Bruce', 'Fred', 'Junior', 'Ralph', 'Alex',
			// 10..15			
			'Albert', 'Bad News', 'Bahh', 'Bells', 'Boing', 'Bubbles',
			// 16..20			
			'Cellos', 'Deranged', 'Good News', 'Hysterical', 'Pipe Organ',
			// 21..23			
			'Trinoids', 'Whisper', 'Zarvox'
		];
		voices.male = ['Bruce', 'Fred', 'Junior', 'Ralph', 'Alex'];
		voices.female = ['Agnes', 'Kathy', 'Princess', 'Vicki', 'Victoria'];
		voices.others = [
			'Albert', 'Bad News', 'Bahh', 'Bells', 'Boing', 'Bubbles',
			'Cellos', 'Deranged', 'Good News', 'Hysterical', 'Pipe Organ',
			'Trinoids', 'Whisper', 'Zarvox'
		];

		defaultVoice = voices.male[0];
		tempPrefix = "temp_speech_";
		tempDir = thisProcess.platform.recordingsDir +/+ "SpeechRenderings";
		File.exists(tempDir).not.if {
			("mkdir -p " + tempDir.escapeChar($ )).systemCmd;
		};

	}
	
	*new { |string, voice, path, opt|
		^super.new.init(string, voice, path, opt)
	}
	
	*cleanUp {
		("rm" + (tempDir.escapeChar($ )) +/+ tempPrefix ++ "*").systemCmd;
		"Removed all temporary buffers from %\n".postf(tempDir);
	}
	
	init { |string, voice, path, opt|
		// start the command
		cmd = "say";

		// add the voice - sybols and strings pass through
		(voice.isNil).if { voice = defaultVoice };
		(voice.class == Integer).if { voice = voices.all[voice] };

		cmd = cmd + "-v" + voice.asString;
		
		// add more options
		opt.isNil.if { opt = "" };
		cmd = cmd + opt;

		// add output file path
		path.isNil.if
			{ filePath = tempDir +/+ tempPrefix ++ Date.localtime.stamp ++ ".aiff" }
			{ filePath = path };
		
		// cmd is ready!!!		
		cmd = cmd + "-o" + (filePath.escapeChar($ )) + string;

		cmd.systemCmd; // works ok? sync for buffer?

		^this
	}
	
	asBuffer { |server|
		server.isNil.if { server = Server.default };
		^Buffer.read(server, filePath)
	}
}

SpeechBuffer : SpeechRender {
	*new { |string, voice, path, opt, server|
		^super.new(string, voice, path, opt).asBuffer(server)
	}
}
raw 2870 chars (focus & ctrl+a+c to copy)
reception
comments