#! /usr/bin/env python

import sys
import audio
import stdwin

import string
import getopt

from stdwinevents import *
from Buttons import *
from Sliders import *
#from Soundogram import Soundogram
from VUMeter import VUMeter
from WindowParent import WindowParent, MainLoop
from HVSplit import HSplit, VSplit

class TimeOutToggleButton(ToggleButton):
	def define(self, parent):
		self = ToggleButton.define(self, parent)
		self.parent.need_timer(self)
		self.timer_hook = 0
		return self
	def timer(self):
		if self.timer_hook:
			self.timer_hook(self)

K = 1024
BUFSIZE = 30*8*K
Rates = [0, 32*K, 16*K, 8*K]
Magics = ['', '0032', '0016', '0008']

class Struct: pass
G = Struct()

def main():
	#
	# Turn off scroll bars
	#
	stdwin.setdefscrollbars(0, 0)
	#
	# Set default state
	#
	G.gain = 60
	G.rate = 3
	G.nomuting = 0
	G.savefile = '@rec'
	#
	# Set default values
	#
	G.data = ''
	G.playing = 0
	G.recording = 0
	G.sogram = 0
	#
	# Parse options
	#
	optlist, args = getopt.getopt(sys.argv[1:], 'mdg:r:')
	#
	for optname, optarg in optlist:
		if 0: # (So all cases start with elif)
			pass
		elif optname == '-d':
			G.debug = 1
		elif optname == '-g':
			G.gain = string.atoi(optarg)
			if not (0 < G.gain < 256):
				raise optarg.error, '-g gain out of range'
		elif optname == '-m':
			G.nomuting = (not G.nomuting)
		elif optname == '-r':
			G.rate = string.atoi(optarg)
			if not (1 <= G.rate <= 3):
				raise optarg.error, '-r rate out of range'
	#
	if args:
		G.savefile = args[0]
	#
	# Initialize the sound package
	#
	audio.setoutgain(G.nomuting * G.gain)	# Silence the speaker
	audio.setrate(G.rate)
	#
	# Create the WindowParent and VSplit
	#
	G.window = WindowParent().create('Recorder', (0, 0))
	w = G.vsplit = VSplit().create(G.window)
	#
	# VU-meter
	#
	G.vubtn = VUMeter().define(w)
	#
	# Radiobuttons for rates
	#
	r1btn = RadioButton().definetext(w, '32 K/sec')
	r1btn.on_hook = rate_hook
	r1btn.rate = 1
	#
	r2btn = RadioButton().definetext(w, '16 K/sec')
	r2btn.on_hook = rate_hook
	r2btn.rate = 2
	#
	r3btn = RadioButton().definetext(w, '8 K/sec')
	r3btn.on_hook = rate_hook
	r3btn.rate = 3
	#
	radios = [r1btn, r2btn, r3btn]
	r1btn.group = r2btn.group = r3btn.group = radios
	for r in radios:
		if r.rate == G.rate: r.select(1)
	#
	# Other controls
	#
	G.recbtn = TimeOutToggleButton().definetext(w, 'Record')
	G.recbtn.on_hook = record_on_hook
	G.recbtn.timer_hook = record_timer_hook
	G.recbtn.off_hook = record_off_hook
	#
	G.mutebtn = CheckButton().definetext(w, 'Mute')
	G.mutebtn.select(not G.nomuting)
	G.mutebtn.hook = mute_hook
	#
	G.playbtn = TimeOutToggleButton().definetext(w, 'Playback')
	G.playbtn.on_hook = play_on_hook
	G.playbtn.timer_hook = play_timer_hook
	G.playbtn.off_hook = play_off_hook
	#
	G.gainbtn = ComplexSlider().define(w)
	G.gainbtn.settexts('  Volume: ', '  ')
	G.gainbtn.setminvalmax(0, G.gain, 255)
	G.gainbtn.sethook(gain_hook)
	#
	G.sizebtn = Label().definetext(w, `len(G.data)` + ' bytes')
	#
	#G.showbtn = PushButton().definetext(w, 'Sound-o-gram...')
	#G.showbtn.hook = show_hook
	#
	G.savebtn = PushButton().definetext(w, 'Save...')
	G.savebtn.hook = save_hook
	#
	G.quitbtn = PushButton().definetext(w, 'Quit')
	G.quitbtn.hook = quit_hook
	G.playbtn.enable(0)
	G.savebtn.enable(0)
	#G.showbtn.enable(0)
	start_vu()
	G.window.realize()
	#
	# Event loop
	#
	MainLoop()

# XXX Disabled...
def show_hook(self):
	savetext = self.text
	self.settext('Be patient...')
	close_sogram()
	stdwin.setdefwinsize(400, 300)
	win = stdwin.open('Sound-o-gram')
	G.sogram = Soundogram().define(win, G.data)
	win.buttons = [G.sogram]
	self.settext(savetext)

def close_sogram():
	if G.sogram:
		# Break circular references
		G.sogram.win.buttons[:] = []
		del G.sogram.win
		G.sogram = 0

def mute_hook(self):
	G.nomuting = (not self.selected)
	audio.setoutgain(G.nomuting * G.gain)

def rate_hook(self):
	G.rate = self.rate
	audio.setrate(G.rate)

def record_on_hook(self):
	stop_vu()
	close_sogram()
	audio.setrate(G.rate)
	audio.setoutgain(G.nomuting * G.gain)
	audio.start_recording(BUFSIZE)
	G.recording = 1
	G.playbtn.enable(0)
	G.window.settimer(10 * BUFSIZE / Rates[G.rate])

def record_timer_hook(self):
	if G.recording:
		if audio.poll_recording():
			self.hilite(0)
			record_off_hook(self)
		else:
			self.parent.settimer(5)

def record_off_hook(self):
	if not G.recording:
		return
	G.data = audio.stop_recording()
	G.recording = 0
	G.sizebtn.settext(`len(G.data)` + ' bytes')
	audio.setoutgain(G.nomuting * G.gain)
	G.playbtn.enable((len(G.data) > 0))
	G.savebtn.enable((len(G.data) > 0))
	#G.showbtn.enable((len(G.data) > 0))
	G.window.settimer(0)
	start_vu()

def play_on_hook(self):
	stop_vu()
	audio.setrate(G.rate)
	audio.setoutgain(G.gain)
	audio.start_playing(G.data)
	G.playing = 1
	G.recbtn.enable(0)
	G.window.settimer(max(10 * len(G.data) / Rates[G.rate], 1))

def play_timer_hook(self):
	if G.playing:
		if audio.poll_playing():
			self.hilite(0)
			play_off_hook(self)
		else:
			self.parent.settimer(5)

def play_off_hook(self):
	if not G.playing:
		return
	x = audio.stop_playing()
	G.playing = 0
	audio.setoutgain(G.nomuting * G.gain)
	G.recbtn.enable(1)
	G.window.settimer(0)
	start_vu()

def gain_hook(self):
	G.gain = self.val
	if G.playing or G.nomuting: audio.setoutgain(G.gain)

def save_hook(self):
	if not G.data:
		stdwin.fleep()
	else:
		prompt = 'Store sampled data on file: '
		try:
			G.savefile = stdwin.askfile(prompt, G.savefile, 1)
		except KeyboardInterrupt:
			return
		try:
			fp = open(G.savefile, 'w')
			fp.write(Magics[G.rate] + G.data)
		except:
			stdwin.message('Cannot create ' + file)

def stop_vu():
	G.vubtn.stop()

def start_vu():
	G.vubtn.start()

def quit_hook(self):
	G.window.delayed_destroy()

try:
	main()
finally:
	audio.setoutgain(0)
