OXYGENHUB
◆ main openmsx / share / scripts / _record_channels.tcl
7.9 KiB · Tcl · last changed chore(): initial commit 1 month ago source blame history raw
1
namespace eval record_channels {
2
3
set_help_text record_channels \
4
{Convenience function to control recording of individual channels of
5
sounddevice(s).
6
7
There are three subcommands (start, stop and list) to respectively start
8
recording additional channels, to stop recording all or some channels and
9
to list which channels are currently being recorded.
10
  record_channels [start] [<device> [<channels>]]
11
  record_channels  stop   [<device> [<channels>]]
12
  record_channels  list
13
When starting recording, you can optionally specify a prefix for the
14
destination file names with the -prefix option.
15
16
Some examples will make it much clearer:
17
  - To start recording:
18
      record_channels start PSG      record all PSG channels
19
      record_channels PSG            the 'start' keyword can be left out
20
      record_channels SCC 1,3-5      only record channels 1 and 3 to 5
21
      record_channels SCC PSG 1      record all SCC channels + PSG channel 1
22
      record_channels all            record all channels of all devices
23
      record_channels all -prefix t  record all channels of all devices using
24
                                     prefix 't'
25
  - To stop recording
26
      record_channels stop           stop all recording
27
      record_channels stop PSG       stop recording all PSG channels
28
      record_channels stop SCC 3,5   stop recording SCC channels 3 and 5
29
  - To show the current status
30
      record_channels list           shows which channels are being recorded
31
}
32
33
set mute_help_text \
34
{Convenience function to control (un)muting of individual channels of
35
soundevice(s).
36
37
Examples:
38
  mute_channels PSG               mute all PSG channels
39
  mute_channels SCC 2,4           mute SCC channels 2 and 4
40
  unmute_channels PSG 1 SCC 3-5   unmute PSG channel 1, SCC channels 3 to 5
41
  mute_channels                   show which channels are currently muted
42
  unmute_channels                 unmute all channels on all devices
43
  solo PSG 2                      mute everything except PSG channel 2
44
}
45
set_help_text mute_channels   $mute_help_text
46
set_help_text unmute_channels $mute_help_text
47
set_help_text solo            $mute_help_text
48
49
set_tabcompletion_proc record_channels [namespace code tab_sounddevice_channels]
50
set_tabcompletion_proc mute_channels   [namespace code tab_sounddevice_channels]
51
set_tabcompletion_proc unmute_channels [namespace code tab_sounddevice_channels]
52
set_tabcompletion_proc solo            [namespace code tab_sounddevice_channels]
53
proc tab_sounddevice_channels {args} {
54
	set result [machine_info sounddevice]
55
	if {([lindex $args 0] eq "record_channels") && ([llength $args] == 2)} {
56
		set result [concat $result "start stop list all"]
57
	}
58
	return $result
59
}
60
61
proc parse_channel_numbers {str} {
62
	set result [list]
63
	foreach a [split $str ", "] {
64
		set b [split $a "-"]
65
		foreach c $b {
66
			if {![string is integer $c]} {
67
				error "Not an integer: $c"
68
			}
69
		}
70
		switch [llength $b] {
71
			0 {}
72
			1 {lappend result [lindex $b 0]}
73
			2 {for {set i [lindex $b 0]} {$i <= [lindex $b 1]} {incr i} {
74
				lappend result $i
75
			}}
76
			default {error "Invalid range: $a"}
77
		}
78
	}
79
	return [lsort -unique $result]
80
}
81
82
proc get_all_channels {device} {
83
	set i 1
84
	set channels [list]
85
	while {[info exists ::${device}_ch${i}_record]} {
86
		lappend channels $i
87
		incr i
88
	}
89
	return $channels
90
}
91
92
proc get_all_devices_all_channels {} {
93
	set result [list]
94
	foreach device [machine_info sounddevice] {
95
		lappend result $device [get_all_channels $device]
96
	}
97
	return $result
98
}
99
100
proc get_recording_channels {} {
101
	set result [list]
102
	set sounddevices [machine_info sounddevice]
103
	foreach device $sounddevices {
104
		set active [list]
105
		foreach ch [get_all_channels $device] {
106
			set var ::${device}_ch${ch}_record
107
			if {[set $var] ne ""} {
108
				lappend active $ch
109
			}
110
		}
111
		if {[llength $active]} {
112
			lappend result "$device: $active"
113
		}
114
	}
115
	return $result
116
}
117
118
proc get_muted_channels {} {
119
	set result [list]
120
	set sounddevices [machine_info sounddevice]
121
	foreach device $sounddevices {
122
		set active [list]
123
		foreach ch [get_all_channels $device] {
124
			set var ::${device}_ch${ch}_mute
125
			if {[set $var]} {
126
				lappend active $ch
127
			}
128
		}
129
		if {[llength $active]} {
130
			lappend result "$device: $active"
131
		}
132
	}
133
	return $result
134
}
135
136
proc parse_device_channels {tokens} {
137
	set sounddevices [machine_info sounddevice]
138
	set device_channels [list]
139
	if {[lindex $tokens 0] == "all"} {
140
		foreach device $sounddevices {
141
			lappend device_channels $device [get_all_channels $device]
142
		}
143
		return $device_channels
144
	}
145
	while {[llength $tokens]} {
146
		set device [lindex $tokens 0]
147
		set tokens [lrange $tokens 1 end]
148
		if {$device ni $sounddevices} {
149
			error "Unknown sounddevice: $device"
150
		}
151
		set range [lindex $tokens 0]
152
		if {($range ne "") && ($range ni $sounddevices)} {
153
			set channels [parse_channel_numbers $range]
154
			set tokens [lrange $tokens 1 end]
155
			foreach ch $channels {
156
				if {![info exists ::${device}_ch${ch}_record]} {
157
					error "No channel $ch on sounddevice $device"
158
				}
159
			}
160
		} else {
161
			set channels [get_all_channels $device]
162
		}
163
		lappend device_channels $device $channels
164
	}
165
	return $device_channels
166
}
167
168
proc record_channels {args} {
169
	set start true
170
	set device_channels [list]
171
172
	# parse subcommand (default is start)
173
	set first [lindex $args 0]
174
	switch $first {
175
		list {
176
			return [join [get_recording_channels] "\n"]
177
		}
178
		start -
179
		stop {
180
			set start [string equal $first "start"]
181
			set args [lrange $args 1 end]
182
		}
183
	}
184
185
	if {$start} {
186
		set prefix [utils::filename_clean [guess_title]]
187
		# see if there's a -prefix option to override the default
188
		set prefix_index [lsearch -exact $args "-prefix"]
189
		if {$prefix_index >= 0 && $prefix_index < ([llength $args] - 1)} {
190
			set prefix [lindex $args [expr {$prefix_index + 1}]]
191
			set args [lreplace $args $prefix_index [expr {$prefix_index + 1}]]
192
		}
193
	}
194
195
	# parse devices/channels
196
	set device_channels [parse_device_channels $args]
197
198
	# stop without any further arguments -> stop all
199
	if {!$start && ![llength $device_channels]} {
200
		foreach device [machine_info sounddevice] {
201
			set channels [get_all_channels $device]
202
			lappend device_channels $device $channels
203
		}
204
	}
205
206
	set retval ""
207
	# actually start/stop recording
208
	foreach {device channels} $device_channels {
209
		foreach ch $channels {
210
			set var ::${device}_ch${ch}_record
211
			if {$start} {
212
				set directory [file normalize $::env(OPENMSX_USER_DATA)/../soundlogs]
213
				# create dir always
214
				file mkdir $directory
215
				set software_section $prefix
216
				if {$software_section ne ""} {
217
					set software_section "${software_section}-"
218
				}
219
				set $var [utils::get_next_numbered_filename $directory "${software_section}${device}-ch${ch}_" ".wav"]
220
				append retval "Recording $device channel $ch to [set $var]...\n"
221
			} else {
222
				if {[set $var] ne ""} {
223
					append retval "Stopped recording $device channel $ch to [set $var]...\n"
224
				}
225
				set $var ""
226
			}
227
		}
228
	}
229
	return $retval
230
}
231
232
proc do_mute_channels {device_channels state} {
233
	foreach {device channels} $device_channels {
234
		foreach ch $channels {
235
			set ::${device}_ch${ch}_mute $state
236
		}
237
	}
238
}
239
240
proc mute_channels {args} {
241
	# parse devices/channels
242
	set device_channels [parse_device_channels $args]
243
244
	# no argumnets specified, list muted channels
245
	if {![llength $device_channels]} {
246
		return [join [get_muted_channels] "\n"]
247
	}
248
249
	# actually mute channels
250
	do_mute_channels $device_channels true
251
}
252
253
proc unmute_channels {args} {
254
	# parse devices/channels
255
	set device_channels [parse_device_channels $args]
256
257
	# no arguments specified, unmute all channels
258
	if {![llength $device_channels]} {
259
		set device_channels [get_all_devices_all_channels]
260
	}
261
262
	#actually unmute channels
263
	do_mute_channels $device_channels false
264
}
265
266
proc solo {args} {
267
	# parse devices/channels
268
	set device_channels [parse_device_channels $args]
269
270
	# mute everything, unmute specified channels
271
	do_mute_channels [get_all_devices_all_channels] true
272
	do_mute_channels $device_channels false
273
}
274
275
namespace export record_channels
276
namespace export mute_channels
277
namespace export unmute_channels
278
namespace export solo
279
280
} ;# namespace record_channels
281
282
namespace import record_channels::*