123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182(*
* Copyright 2011 The Savonet Team
*
* This file is part of ocaml-mm.
*
* ocaml-mm is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* ocaml-mm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with ocaml-mm; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* As a special exception to the GNU Library General Public License, you may
* link, statically or dynamically, a "work that uses the Library" with a publicly
* distributed version of the Library to produce an executable file containing
* portions of the Library, and distribute that executable file under terms of
* your choice, without any of the additional requirements listed in clause 6
* of the GNU Library General Public License.
* By "a publicly distributed version of the Library", we mean either the unmodified
* Library as distributed by The Savonet Team, or a modified version of the Library that is
* distributed under the conditions defined in clause 3 of the GNU Library General
* Public License. This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU Library General Public License.
*
*)openMm_audioclasstypet=objectmethodset_volume:float->unitmethodnote_on:int->float->unitmethodnote_off:int->float->unitmethodfill_add:Audio.buffer->int->int->unitmethodplay_add:MIDI.buffer->int->Audio.buffer->int->int->unitmethodplay:MIDI.buffer->int->Audio.buffer->int->int->unitmethodreset:unitendtypesynth=ttypenote={note:int;volume:float;generator:Audio.Generator.t}classvirtualbase=object(self)methodvirtualprivategenerator:float->float->Audio.Generator.tvalmutablevol:float=1.methodset_volumev=vol<-vvalmutablenotes:notelist=[]methodnote_onnv=letnote={note=n;volume=v;(* TODO: we could want to change the volume after a not has begun to be played *)generator=self#generator(Audio.Note.freqn)(v*.vol);}innotes<-note::notesmethodnote_offn_=(* TODO: remove only one note *)(* TODO: merge the two iterations on the list *)List.iter(funnote->ifnote.note=nthennote.generator#release)notes;notes<-List.filter(funnote->notnote.generator#dead)notesmethodfill_addbufofslen=List.iter(funnote->note.generator#fill_addbufofslen)notesmethodprivatefillbufofslen=Audio.clearbufofslen;self#fill_addbufofslenmethodprivateevent=function|MIDI.Note_off(n,v)->self#note_offnv|MIDI.Note_on(n,v)->self#note_onnv|MIDI.Control_change(0x7,v)->self#set_volume(floatv/.127.)|_->()(* TODO: add offset for evs *)methodplay_addevseofsbufbofslen=letrecplayoevsofs=matchevswith|(t,_)::_whent>=eofs+len->()|(t,_)::tlwhent<eofs->playttlofs|(t,e)::tl->letdelta=t-maxeofsoinself#fill_addbuf(bofs+ofs)delta;self#evente;playttl(ofs+delta)|[]->self#fill_addbuf(bofs+ofs)(len-o)inplay0(MIDI.dataevs)0methodplayevseofsbufbofslen=Audio.clearbufbofslen;self#play_addevseofsbufbofslenmethodreset=notes<-[]endclasscreateg=objectinheritbasemethodprivategeneratorfv=gfvendclasscreate_monog=create(funfv->newAudio.Generator.of_mono(gfv))letmight_adsradsrg=matchadsrwithNone->g|Somea->newAudio.Mono.Generator.adsragclasssine?adsrsr=create_mono(funfv->might_adsradsr(newAudio.Mono.Generator.sinesr~volume:vf))classsquare?adsrsr=create_mono(funfv->might_adsradsr(newAudio.Mono.Generator.squaresr~volume:vf))classsaw?adsrsr=create_mono(funfv->might_adsradsr(newAudio.Mono.Generator.sawsr~volume:vf))classmonophonic(g:Audio.Generator.t)=object(self)methodset_volumev=g#set_volumevmethodnote_onnv=g#set_frequency(Audio.Note.freqn);g#set_volumevmethodnote_off(_:int)(_:float)=(* TODO: check for the last note? *)g#releasemethodfill_add=g#fill_addmethodplay_add(_:MIDI.buffer)(_:int)(_:Audio.buffer)(_:int)(_:int):unit=assertfalsemethodplayevseofsbufbofslen:unit=self#play_addevseofsbufbofslen;assertfalsemethodreset=g#set_volume0.endmoduleMultitrack=structclasstypet=objectmethodplay_add:MIDI.Multitrack.buffer->int->Audio.buffer->int->int->unitmethodplay:MIDI.Multitrack.buffer->int->Audio.buffer->int->int->unitendclasscreaten(f:int->synth)=object(self)valsynth=Array.initnfmethodplay_add(evs:MIDI.Multitrack.buffer)eofsbufbofslen=forc=0toArray.lengthsynth-1dosynth.(c)#play_addevs.(c)eofsbufbofslendonemethodplayevseofsbufbofslen=Audio.clearbufbofslen;self#play_addevseofsbufbofslenendend