History

added aubioweb added aubioweb

Paul Brossier committed 20 years ago

Commit 51402761 parent 660c1d84 files · +567 −0Browse files

Showing the changes to python (4 of 4 files). Show all

Unified · Split

4 files
  1. python/aubio/web/__init__.py +0 −0
  2. python/aubio/web/browser.py +167 −0
  3. python/aubio/web/html.py +287 −0
  4. python/aubioweb.py +113 −0
python/aubio/web/__init__.py+0−0

Empty file added.

python/aubio/web/browser.py+167−0
@@ -0,0 +1,167 @@
1+ #
2+ # Copyright 2004 Apache Software Foundation
3+ #
4+ # Licensed under the Apache License, Version 2.0 (the "License"); you
5+ # may not use this file except in compliance with the License. You
6+ # may obtain a copy of the License at
7+ #
8+ # http://www.apache.org/licenses/LICENSE-2.0
9+ #
10+ # Unless required by applicable law or agreed to in writing, software
11+ # distributed under the License is distributed on an "AS IS" BASIS,
12+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13+ # implied. See the License for the specific language governing
14+ # permissions and limitations under the License.
15+ #
16+ # Originally developed by Gregory Trubetskoy.
17+ #
18+ # $Id: publisher.py,v 1.36 2004/02/16 19:47:27 grisha Exp $
19+
20+"""
21+ This handler is conceputally similar to Zope's ZPublisher, except
22+ that it:
23+
24+ 1. Is written specifically for mod_python and is therefore much faster
25+ 2. Does not require objects to have a documentation string
26+ 3. Passes all arguments as simply string
27+ 4. Does not try to match Python errors to HTTP errors
28+ 5. Does not give special meaning to '.' and '..'.
29+
30+ This is a modified version of mod_python.publisher.handler Only the first
31+ directory argument is matched, the rest is left for path_info. A default
32+ one must be provided.
33+
34+"""
35+
36+from mod_python import apache
37+from mod_python import util
38+from mod_python.publisher import resolve_object,process_auth,imp_suffixes
39+
40+import sys
41+import os
42+import re
43+
44+from types import *
45+
46+def configure_handler(req,default):
47+
48+ req.allow_methods(["GET", "POST"])
49+ if req.method not in ["GET", "POST"]:
50+ raise apache.SERVER_RETURN, apache.HTTP_METHOD_NOT_ALLOWED
51+
52+ func_path = ""
53+ if req.path_info:
54+ func_path = req.path_info[1:] # skip first /
55+ #func_path = func_path.replace("/", ".")
56+ #if func_path[-1:] == ".":
57+ # func_path = func_path[:-1]
58+ # changed: only keep the first directory
59+ func_path = re.sub('/.*','',func_path)
60+
61+ # default to 'index' if no path_info was given
62+ if not func_path:
63+ func_path = "index"
64+
65+ # if any part of the path begins with "_", abort
66+ if func_path[0] == '_' or func_path.count("._"):
67+ raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
68+
69+ ## import the script
70+ path, module_name = os.path.split(req.filename)
71+ if not module_name:
72+ module_name = "index"
73+
74+ # get rid of the suffix
75+ # explanation: Suffixes that will get stripped off
76+ # are those that were specified as an argument to the
77+ # AddHandler directive. Everything else will be considered
78+ # a package.module rather than module.suffix
79+ exts = req.get_addhandler_exts()
80+ if not exts:
81+ # this is SetHandler, make an exception for Python suffixes
82+ exts = imp_suffixes
83+ if req.extension: # this exists if we're running in a | .ext handler
84+ exts += req.extension[1:]
85+ if exts:
86+ suffixes = exts.strip().split()
87+ exp = "\\." + "$|\\.".join(suffixes)
88+ suff_matcher = re.compile(exp) # python caches these, so its fast
89+ module_name = suff_matcher.sub("", module_name)
90+
91+ # import module (or reload if needed)
92+ # the [path] argument tells import_module not to allow modules whose
93+ # full path is not in [path] or below.
94+ config = req.get_config()
95+ autoreload=int(config.get("PythonAutoReload", 1))
96+ log=int(config.get("PythonDebug", 0))
97+ try:
98+ module = apache.import_module(module_name,
99+ autoreload=autoreload,
100+ log=log,
101+ path=[path])
102+ except ImportError:
103+ et, ev, etb = sys.exc_info()
104+ # try again, using default module, perhaps this is a
105+ # /directory/function (as opposed to /directory/module/function)
106+ func_path = module_name
107+ module_name = "index"
108+ try:
109+ module = apache.import_module(module_name,
110+ autoreload=autoreload,
111+ log=log,
112+ path=[path])
113+ except ImportError:
114+ # raise the original exception
115+ raise et, ev, etb
116+
117+ # does it have an __auth__?
118+ realm, user, passwd = process_auth(req, module)
119+
120+ # resolve the object ('traverse')
121+ try:
122+ object = resolve_object(req, module, func_path, realm, user, passwd)
123+ except AttributeError:
124+ # changed, return the default path instead
125+ #raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
126+ object = default
127+ # not callable, a class or an unbound method
128+ if (not callable(object) or
129+ type(object) is ClassType or
130+ (hasattr(object, 'im_self') and not object.im_self)):
131+
132+ result = str(object)
133+
134+ else:
135+ # callable, (but not a class or unbound method)
136+
137+ # process input, if any
138+ req.form = util.FieldStorage(req, keep_blank_values=1)
139+
140+ result = util.apply_fs_data(object, req.form, req=req)
141+
142+ if result or req.bytes_sent > 0 or req.next:
143+
144+ if result is None:
145+ result = ""
146+ else:
147+ result = str(result)
148+
149+ # unless content_type was manually set, we will attempt
150+ # to guess it
151+ if not req._content_type_set:
152+ # make an attempt to guess content-type
153+ if result[:100].strip()[:6].lower() == '<html>' \
154+ or result.find('</') > 0:
155+ req.content_type = 'text/html'
156+ else:
157+ req.content_type = 'text/plain'
158+
159+ if req.method != "HEAD":
160+ req.write(result)
161+ else:
162+ req.write("")
163+ return apache.OK
164+ else:
165+ req.log_error("mod_python.publisher: %s returned nothing." % `object`)
166+ return apache.HTTP_INTERNAL_SERVER_ERROR
167+
python/aubio/web/html.py+287−0
@@ -0,0 +1,287 @@
1+from aubio.bench.node import *
2+
3+def parse_args(req):
4+ req.basehref = BASEHREF
5+ req.datadir = DATADIR
6+ if req.path_info: path_info = req.path_info
7+ else: path_info = '/'
8+ location = re.sub('^/show_[a-z0-9]*/','',path_info)
9+ location = re.sub('^/play_[a-z0-9]*/','',location)
10+ location = re.sub('^/index/','',location)
11+ location = re.sub('^/','',location)
12+ location = re.sub('/$','',location)
13+ datapath = "%s/%s" % (DATADIR,location)
14+ respath = "%s/%s" % (DATADIR,location)
15+ last = re.sub('/$','',location)
16+ last = last.split('/')[-1]
17+ first = path_info.split('/')[1]
18+ # store some of this in the mp_request
19+ req.location, req.datapath, req.respath = location, datapath, respath
20+ req.first, req.last = first, last
21+
22+ if location:
23+ if not (os.path.isfile(datapath) or
24+ os.path.isdir(datapath) or
25+ location in ['feedback','email']):
26+ # the path was not understood
27+ from mod_python import apache
28+ req.write("<html> path not found %s</html>" % (datapath))
29+ raise apache.SERVER_RETURN, apache.OK
30+ #from mod_python import apache
31+ #raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
32+
33+def navigation(req):
34+ """ main html navigation header """
35+ from mod_python import psp
36+ req.content_type = "text/html"
37+ parse_args(req)
38+ datapath = req.datapath
39+ location = req.location
40+
41+ # deal with session
42+ if req.sess.is_new():
43+ msg = "<b>Welcome %s</b><br>" % req.sess['login']
44+ else:
45+ msg = "<b>Welcome back %s</b><br>" % req.sess['login']
46+
47+ # start writing
48+ tmpl = psp.PSP(req, filename='header.tmpl')
49+ tmpl.run(vars = { 'title': "aubioweb / %s / %s" % (req.first,location),
50+ 'basehref': '/~piem/',
51+ 'message': msg,
52+ 'action': req.first})
53+
54+ req.write("<h2>Content of ")
55+ print_link(req,"","/")
56+ y = location.split('/')
57+ for i in range(len(y)-1):
58+ print_link(req,"/".join(y[:i+1]),y[i])
59+ req.write(" / ")
60+ req.write("%s</h2>\n" % y[-1])
61+
62+ a = {'show_info' : 'info',
63+ 'show_sound': 'waveform',
64+ 'show_onset': 'onset',
65+ 'index' : 'index',
66+ 'show_pitch': 'pitch',
67+ 'play_m3u': 'stream (m3u/ogg)',
68+ 'play_ogg': 'save (ogg)',
69+ 'play_wav': 'save (wav)',
70+ }
71+
72+ # print task lists (only remaining tasks)
73+ print_link(req,re.sub('%s.*'%req.last,'',location),"go up")
74+ akeys = a.keys(); akeys.sort();
75+ curkey = req.first
76+ for akey in akeys:
77+ if akey != curkey:
78+ req.write(":: ")
79+ print_link(req,"/".join((akey,location)),a[akey])
80+ else:
81+ req.write(":: ")
82+ req.write("<b>%s</b>" % a[akey])
83+ req.write("<br>")
84+
85+ # list the content of the directories
86+ listdir,listfiles = [],[]
87+ if os.path.isdir(datapath):
88+ listfiles = list_snd_files(datapath)
89+ listdir = list_dirs(datapath)
90+ listdir.pop(0) # kick the current dir
91+ elif os.path.isfile(datapath):
92+ listfiles = [datapath]
93+ listdir = [re.sub(req.last,'',location)]
94+
95+ link_list(req,listdir,title="Subdirectories")
96+ link_list(req,listfiles,title="Files")
97+
98+def footer(req):
99+ """ html navigation footer """
100+ from mod_python import psp
101+ tmpl = psp.PSP(req, filename='footer.tmpl')
102+ tmpl.run(vars = { 'time': -req.mtime+req.request_time })
103+
104+def apply_on_data(req, func,**keywords):
105+ # bug: hardcoded snd file filter
106+ act_on_data(func,req.datapath,req.respath,
107+ filter="f -maxdepth 1 -name '*.wav' -o -name '*.aif'",**keywords)
108+
109+def print_link(req,target,name,basehref=BASEHREF):
110+ req.write("<a href='%s/%s'>%s</a>\n" % (basehref,target,name))
111+
112+def print_img(req,target,name='',basehref=BASEHREF):
113+ if name == '': name = target
114+ req.write("<img src='%s/%s' alt='%s' title='%s'>\n" % (basehref,target,name,name))
115+
116+def link_list(req,targetlist,basehref=BASEHREF,title=None):
117+ if len(targetlist) > 1:
118+ if title: req.write("<h3>%s</h3>"%title)
119+ req.write('<ul>')
120+ for i in targetlist:
121+ s = re.split('%s/'%DATADIR,i,maxsplit=1)[1]
122+ if s:
123+ req.write('<li>')
124+ print_link(req,s,s)
125+ req.write('</li>')
126+ req.write('</ul>')
127+
128+def print_list(req,list):
129+ req.write("<pre>\n")
130+ for i in list: req.write("%s\n" % i)
131+ req.write("</pre>\n")
132+
133+def print_command(req,command):
134+ req.write("<h4>%s</h4>\n" % re.sub('%%','%',command))
135+ def print_runcommand(input,output):
136+ cmd = re.sub('(%)?%i','%s' % input, command)
137+ cmd = re.sub('(%)?%o','%s' % output, cmd)
138+ print_list(req,runcommand(cmd))
139+ apply_on_data(req,print_runcommand)
140+
141+def datapath_to_location(input):
142+ location = re.sub(DATADIR,'',input)
143+ return re.sub('^/*','',location)
144+
145+## drawing hacks
146+def draw_func(req,func):
147+ import re
148+ req.content_type = "image/png"
149+ # build location (strip the func_path, add DATADIR)
150+ location = re.sub('^/draw_[a-z]*/','%s/'%DATADIR,req.path_info)
151+ location = re.sub('.png$','',location)
152+ if not os.path.isfile(location):
153+ from mod_python import apache
154+ raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
155+ # replace location in func
156+ cmd = re.sub('(%)?%i','%s' % location, func)
157+ # add PYTHONPATH at the beginning,
158+ cmd = "%s%s 2> /dev/null" % (PYTHONPATH,cmd)
159+ for each in runcommand(cmd):
160+ req.write("%s\n"%each)
161+
162+def show_task(req,task):
163+ def show_task_file(input,output,task):
164+ location = datapath_to_location(input)
165+ print_img(req,"draw_%s/%s" % (task,location))
166+ navigation(req)
167+ req.write("<h3>%s</h3>\n" % task)
168+ apply_on_data(req,show_task_file,task=task)
169+ footer(req)
170+
171+## waveform_foo
172+def draw_sound(req):
173+ #draw_func(req,"aubioplot-audio %%i stdout 2> /dev/null")
174+ draw_func(req,"gdtest %%i 2> /dev/null")
175+
176+def show_sound(req):
177+ show_task(req,"sound")
178+
179+## pitch foo
180+def draw_pitch(req,threshold='0.3'):
181+ draw_func(req,"aubiopitch -i %%i -p -m yin -t %s -O stdout -B 2048 -H 1024" % threshold)
182+
183+def show_pitch(req):
184+ show_task(req,"pitch")
185+
186+## onset foo
187+def draw_onset(req,threshold='0.3'):
188+ draw_func(req,"aubiocut -i %%i -p -m complex -t %s -O stdout" % threshold)
189+
190+def show_onset(req,threshold='0.3',details=''):
191+ def onset_file(input,output):
192+ location = datapath_to_location(input)
193+ print_img(req,"draw_onset/%s?threshold=%s"%(location,threshold))
194+ print_link(req,"?threshold=%s" % (float(threshold)-0.1),"-")
195+ req.write("%s\n" % threshold)
196+ print_link(req,"?threshold=%s" % (float(threshold)+0.1),"+")
197+ # bug: hardcoded sndfile extension
198+ anote = re.sub('\.wav$','.txt',input)
199+ if anote == input: anote = ""
200+ res = get_extract(input,threshold)
201+ if os.path.isfile(anote):
202+ tru = get_anote(anote)
203+ print_list(req,get_results(tru,res,0.05))
204+ else:
205+ req.write("no ground truth found<br>\n")
206+ if details:
207+ req.write("<h4>Extraction</h4>\n")
208+ print_list(req,res)
209+ else:
210+ req.write("<a href='%s/show_onset/%s?details=yes&amp;threshold=%s'>details</a><br>\n" %
211+ (req.basehref,location,threshold))
212+ if details and os.path.isfile(anote):
213+ req.write("<h4>Computed differences</h4>\n")
214+ ldiffs = get_diffs(tru,res,0.05)
215+ print_list(req,ldiffs)
216+ req.write("<h4>Annotations</h4>\n")
217+ print_list(req,tru)
218+ navigation(req)
219+ req.write("<h3>Onset</h3>\n")
220+ apply_on_data(req,onset_file)
221+ footer(req)
222+
223+def get_anote(anote):
224+ import aubio.onsetcompare
225+ # FIXME: should import with txtfile.read_datafile
226+ return aubio.onsetcompare.load_onsets(anote)
227+
228+def get_diffs(anote,extract,tol):
229+ import aubio.onsetcompare
230+ return aubio.onsetcompare.onset_diffs(anote,extract,tol)
231+
232+def get_extract(datapath,threshold='0.3'):
233+ cmd = "%saubiocut -v -m complex -t %s -i %s" % (PYTHONPATH,threshold,datapath)
234+ lo = runcommand(cmd)
235+ for i in range(len(lo)): lo[i] = float(lo[i])
236+ return lo
237+
238+def get_results(anote,extract,tol):
239+ import aubio.onsetcompare
240+ orig, missed, merged, expc, bad, doubled = aubio.onsetcompare.onset_roc(anote,extract,tol)
241+ s =("GD %2.8f\t" % (100*float(orig-missed-merged)/(orig)),
242+ "FP %2.8f\t" % (100*float(bad+doubled)/(orig)) ,
243+ "GD-merged %2.8f\t" % (100*float(orig-missed)/(orig)) ,
244+ "FP-pruned %2.8f\t" % (100*float(bad)/(orig)) )
245+ return s
246+
247+# play m3u foo
248+def play_m3u(req):
249+ def show_task_file(input,output,task):
250+ location = datapath_to_location(input)
251+ req.write("http://%s%s/play_ogg/%s\n" % (HOSTNAME,BASEHREF,re.sub("play_m3u",task,location)))
252+ req.content_type = "audio/mpegurl"
253+ parse_args(req)
254+ apply_on_data(req,show_task_file,task="play_ogg")
255+
256+# play wav foo
257+def play_wav(req):
258+ req.content_type = "audio/x-wav"
259+ func = "cat %%i"
260+ # build location (strip the func_path, add DATADIR)
261+ location = re.sub('^/play_wav/','%s/'%DATADIR,req.path_info)
262+ if not os.path.isfile(location):
263+ from mod_python import apache
264+ raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
265+ # replace location in func
266+ cmd = re.sub('(%)?%i','%s' % location, func)
267+ # add PYTHONPATH at the beginning,
268+ cmd = "%s 2> /dev/null" % cmd
269+ for each in runcommand(cmd):
270+ req.write("%s\n"%each)
271+
272+# play ogg foo
273+def play_ogg(req):
274+ req.content_type = "application/ogg"
275+ func = "oggenc -o - %%i"
276+ # build location (strip the func_path, add DATADIR)
277+ location = re.sub('^/play_ogg/','%s/'%DATADIR,req.path_info)
278+ location = re.sub('.ogg$','',location)
279+ if not os.path.isfile(location):
280+ from mod_python import apache
281+ raise apache.SERVER_RETURN, apache.HTTP_NOT_FOUND
282+ # replace location in func
283+ cmd = re.sub('(%)?%i','%s' % location, func)
284+ # add PYTHONPATH at the beginning,
285+ cmd = "%s 2> /dev/null" % cmd
286+ for each in runcommand(cmd):
287+ req.write("%s\n"%each)
python/aubioweb.py+113−0
@@ -0,0 +1,113 @@
1+#!/usr/bin/python
2+
3+doc = """
4+This script works with mod_python to browse a collection of annotated wav files
5+and results.
6+
7+you will need to have at least the following packages need to be installed (the
8+name of the command line tool is precised in parenthesis):
9+
10+libapache-mod-python (apache2 prefered)
11+sndfile-programs (sndfile-info)
12+vorbis-tools (oggenc)
13+python-gnuplot
14+python-numarray
15+
16+Try the command line tools in aubio/python to test your installation.
17+
18+NOTE: this script is probably horribly insecure.
19+
20+example configuration for apache to put in your preferred virtual host.
21+
22+<Directory /home/piem/public_html/aubioweb>
23+ # Minimal config
24+ AddHandler mod_python .py
25+ # Disable these in production
26+ PythonDebug On
27+ PythonAutoReload on
28+ # Default handler in url
29+ PythonHandler aubioweb
30+ ## Authentication stuff (optional)
31+ #PythonAuthenHandler aubioweb
32+ #AuthType Basic
33+ #AuthName "Restricted Area"
34+ #require valid-user
35+ # make default listing
36+ DirectoryIndex aubioweb/
37+</Directory>
38+
39+"""
40+
41+from aubio.web.html import *
42+
43+def handler(req):
44+ from aubio.web.browser import *
45+ from mod_python import Session
46+ req.sess = Session.Session(req)
47+ req.sess['login']='new aubio user'
48+ req.sess.save()
49+ return configure_handler(req,index)
50+
51+def index(req,threshold='0.3'):
52+ navigation(req)
53+ print_command(req,"sfinfo %%i")
54+ return footer(req)
55+
56+def show_info(req,verbose=''):
57+ navigation(req)
58+ print_command(req,"sndfile-info %%i")
59+ return footer(req)
60+
61+def feedback(req):
62+ navigation(req)
63+ req.write("""
64+ Please provide feedback below:
65+ <p>
66+ <form action="/~piem/aubioweb/email" method="POST">
67+ Name: <input type="text" name="name"><br>
68+ Email: <input type="text" name="email"><br>
69+ Comment: <textarea name="comment" rows=4 cols=20></textarea><br>
70+ <input type="submit">
71+ </form>
72+ """)
73+
74+WEBMASTER='piem@calabaza'
75+SMTP_SERVER='localhost'
76+
77+def email(req,name,email,comment):
78+ import smtplib
79+ # make sure the user provided all the parameters
80+ if not (name and email and comment):
81+ return "A required parameter is missing, \
82+ please go back and correct the error"
83+ # create the message text
84+ msg = """\
85+From: %s
86+Subject: feedback
87+To: %s
88+
89+I have the following comment:
90+
91+%s
92+
93+Thank You,
94+
95+%s
96+
97+""" % (email, WEBMASTER, comment, name)
98+ # send it out
99+ conn = smtplib.SMTP(SMTP_SERVER)
100+ try:
101+ conn.sendmail(email, [WEBMASTER], msg)
102+ except smtplib.SMTPSenderRefused:
103+ return """<html>please provide a valid email</html>"""
104+
105+ conn.quit()
106+ # provide feedback to the user
107+ s = """\
108+<html>
109+Dear %s,<br>
110+Thank You for your kind comments, we
111+will get back to you shortly.
112+</html>""" % name
113+ return s

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t