Hello,
Why is it possible to specify the midi channel in the playmidi$ string, since there is no manner to play multiple channels in same time ?
We cannot program a melody line and an accompaniment line (chords).
Yes you can. It's in the documentation.
http://www.ionicwind.com/guides/emergence/writing_programs_music_and_sound.htm
Paul.
Thank you. I know this page.
But how can i play multiple midi channels simoultaneously ?
The following code plays the notes on channel 1 after the notes on channel 0.
I hope you get my point.
PLAYMIDI$ "T90 N0 O5 C2 D2 E2 N1 O5 E2 F2 G2"
Yes, which is why I referred you to the documentation.
PLAYMIDI$ "T120 N0 I25 O5 *C2EG;D4EFG*A2O6CE; *C2EG;D4EFG*A2O7CE;"
Everything between * and ; are played at the same time index and can be on different channels. "*N1C2N0EG;"
Please give me an exemple of code that enables playing a melody on a channel and chords on an other channel. The melody has a different rhythm than the chords.
Quite demanding aren't we.
I gave you an example, 3 notes on 2 different channels "*N1C2N0EG;". Rhythm has no meaning to midi, the time constant does, just like in sheet music. If your base line is playing half notes and your melody line is playing quarter notes at 4/4 time then your base line has a different rhythm. Insert rests where needed.
playmidi$ is meant for simple midi music for your programs, it isn't a midi studio or cakewalk.
Here is a very old program, by Jolly Roger, the start of a midi composer for playmidi$. Perhaps you can learn something from it. Please note however, that I didn't write it so you'll need to ask the original author any questions.
'Midi$ composer
'Creates EBASIC subroutines which play midi music when called
'Jolly Roger Sept 2004
AUTODEFINE "OFF"
DEF win:WINDOW
DEF text,notename$[12],instrumentname$[128]:STRING
DEF run:INT
DEF selectednote,notepitch[100],noteduration[100],noteinstrument[100],numberofnotes:INT
DEF notenumber,notevolume[100],noteisarest[100],noteisachord[100],n:INT
DEF playallbutton,deletebutton,insertbutton,uparrow,downarrow,leftarrow,rightarrow:INT
DEF instrumentcombobox,insertpausebutton,staffscrollbar,numberofnoteatleft:INT
DEF laststaffscrollbarposn,volumescrollbar,textbox1,textbox2,textbox3,textbox4,textbox5:INT
DEF playvisiblenotesbutton,stopmusicbutton,textbox6,temposcrollbar,chordradiobutton:INT
DEF noteradiobutton,endnote,selectednotevisible:INT
DEF left,top,width,height,usablewidth,usableheight:INT
DEF linespacing,maxnotewidth,lowertempo,uppertempo:INT
DEF xmult,ymult:FLOAT
DEF midi$pointer:POINTER
DEF midi$[10000]:ISTRING
CONST blue=0xA00000
numberofnotes=1:selectednote=0
notepitch[0]=24:noteduration[0]=12:noteinstrument[0]=0:noteisarest[0]=0:noteisachord[0]=0
midi$pointer=0
notevolume[0]=64
lowertempo=80:uppertempo=120
playallbutton=3:deletebutton=2:insertbutton=1:instrumentcombobox=4:insertpausebutton=5
staffscrollbar=6:volumescrollbar=7:textbox1=8:textbox2=9:textbox3=10:textbox4=11:textbox5=12
playvisiblenotesbutton=13:stopmusicbutton=14:textbox6=15
chordradiobutton=17:noteradiobutton=18
uparrow=0x26:downarrow=0x28:leftarrow=0x25:rightarrow=0x27
numberofnoteatleft=0:laststaffscrollbarposn=0
notename$="C","C#","D","D#","E","F","F#","G","G#","A","A#","B"
OPENWINDOW win,0, 0, 400, 300,@MAXBOX|@MAXIMIZED, 0, "MIDI$ Composer", &main
GETSIZE win,left,top,width,height
GETSCREENSIZE width,n
CLOSEWINDOW win
WAITUNTIL win=0
OPENWINDOW win,0, 0,width,height,@MINBOX|@NOAUTODRAW, 0, "MIDI$ Composer", &main
'Create file menu
BEGINMENU win
MENUTITLE "&File"
MENUITEM "Load music",0, 1
MENUITEM "Save music", 0, 2
MENUITEM "Create midi$ subroutine", 0, 3
ENDMENU
'Find usable window area
GETCLIENTSIZE win,left,top,usablewidth,usableheight
xmult=usablewidth/634.0:ymult=usableheight/411.0
linespacing=10*ymult
'Ensure linespacing is an even number
IF linespacing%2<>0 THEN linespacing=linespacing-1
maxnotewidth=FLOOR(usablewidth/16)
'Ensure maxnotewidth is an even number
IF maxnotewidth%2<>0 THEN maxnotewidth=maxnotewidth-1
GOSUB fillinstrumentarray
GOSUB createcontrols
GOSUB drawstaff
GOSUB drawnotes
GOSUB enableordisablecontrols
run = 1
WAITUNTIL run = 0
CLOSEWINDOW win
END
SUB main
SELECT @CLASS
CASE @IDCLOSEWINDOW
run = 0
CASE @IDPAINT
GOSUB drawnotesymbol
GOSUB drawstaff
GOSUB drawnotes
CASE @IDMENUPICK
'Click on an item in the file menu
SELECT @MENUNUM
CASE 1
GOSUB loadmusic
CASE 2
GOSUB savemusic
CASE 3
GOSUB savemidi$assubroutine
ENDSELECT
CASE @IDHSCROLL
GOSUB scrollbarshandler
CASE @IDLBUTTONDN
'Left clicked
IF @MOUSEY<16*linespacing
'Click on staff
IF FLOOR(@mousex/maxnotewidth)+numberofnoteatleft<numberofnotes
selectednote=FLOOR(@mousex/maxnotewidth)+numberofnoteatleft
IF noteisarest[selectednote]=0
'Note selected.Set note parameter controls
SETSELECTED win,instrumentcombobox,noteinstrument[selectednote]
SETSCROLLPOS win,volumescrollbar,notevolume[selectednote]
SETSTATE win,chordradiobutton,noteisachord[selectednote]
SETSTATE win,noteradiobutton,1-noteisachord[selectednote]
ENDIF
GOSUB drawnotes
GOSUB enableordisablecontrols
ENDIF
ENDIF
CASE @IDKEYDOWN
SELECT @CODE
CASE uparrow
IF notepitch[selectednote]<47 & selectednotevisible
GOSUB eraseselectednote
notepitch[selectednote]=notepitch[selectednote]+1
GOSUB drawnotes
ENDIF
CASE downarrow
IF notepitch[selectednote]>0 & selectednotevisible
GOSUB eraseselectednote
notepitch[selectednote]=notepitch[selectednote]-1
GOSUB drawnotes
ENDIF
CASE leftarrow
IF noteduration[selectednote]<72 & selectednotevisible
IF noteduration[selectednote]%3=0
noteduration[selectednote]=(noteduration[selectednote]*4)/3
ELSE
noteduration[selectednote]=(noteduration[selectednote]*3)/2
ENDIF
GOSUB eraseselectednote
GOSUB drawnotes
ENDIF
CASE rightarrow
IF noteduration[selectednote]>2 & selectednotevisible
IF noteduration[selectednote]%3<>0
noteduration[selectednote]=(noteduration[selectednote]*3)/4
ELSE
noteduration[selectednote]=(noteduration[selectednote]*2)/3
ENDIF
GOSUB eraseselectednote
GOSUB drawnotes
ENDIF
ENDSELECT
CASE @IDCONTROL
IF @CONTROLID=instrumentcombobox
IF @NOTIFYCODE=@CBNSELCHANGE
'User has selected new instrument
noteinstrument[selectednote]=GETSELECTED (win,instrumentcombobox)
'Return focus to window else won't detect keypresses
SETFOCUS win
ENDIF
ENDIF
IF @CONTROLID=playallbutton
GOSUB createmidi$(0,numberofnotes-1)
midi$pointer=PLAYMIDI$(midi$,TRUE)
'Enable stop music button
ENABLECONTROL win,stopmusicbutton,1
'Return focus to window else won't detect keypresses
SETFOCUS win
ENDIF
IF @CONTROLID=stopmusicbutton
IF midi$pointer<>0
STOPMIDI$ midi$pointer
ENDIF
'Return focus to window else won't detect keypresses
SETFOCUS win
ENDIF
IF @CONTROLID=playvisiblenotesbutton
endnote=numberofnoteatleft+15
IF endnote>numberofnotes-1 THEN endnote=numberofnotes-1
GOSUB createmidi$(selectednote,endnote)
midi$pointer=PLAYMIDI$(midi$,TRUE)
'Enable stop music button
ENABLECONTROL win,stopmusicbutton,1
'Return focus to window else won't detect keypresses
SETFOCUS win
ENDIF
IF @CONTROLID=insertbutton | @CONTROLID=insertpausebutton
GOSUB insertnoteorrest
'Return focus to window else won't detect keypresses
SETFOCUS win
ENDIF
IF @CONTROLID=deletebutton
GOSUB deletenoteorrest
'Return focus to window else won't detect keypresses
SETFOCUS win
ENDIF
IF @CONTROLID=noteradiobutton |@CONTROLID=chordradiobutton
'Set noteisachord[] to 1 if chord radio button selected,0 if it isn't
noteisachord[selectednote]=GETSTATE(win,chordradiobutton)
'Return focus to window else won't detect keypresses
SETFOCUS win
ENDIF
ENDSELECT
RETURN
ENDSUB
SUB drawstaff
'Draw white rectangles with black borders to erase notes
RECT win,-1,-1,usablewidth+2,3*linespacing+2,0,0xFFFFFF
RECT win,-1,3*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,4*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,5*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,6*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,7*linespacing,usablewidth+2,2*linespacing+1,0,0xFFFFFF
RECT win,-1,9*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,10*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,11*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,12*linespacing,usablewidth+2,linespacing+1,0,0xFFFFFF
RECT win,-1,13*linespacing,usablewidth+2,3*linespacing+1,0,0xFFFFFF
RETURN
ENDSUB
SUB createcontrols
DEF instrument,radiobuttonheight:INT
DEF text$:STRING
'Create scroll bars
CONTROL win,@SCROLLBAR,"",0,16*linespacing,usablewidth,2*linespacing,@CTSCROLLHORIZ,staffscrollbar
SETSCROLLRANGE win,staffscrollbar,0,0
CONTROL win,@SCROLLBAR,"",450*xmult,23*linespacing,180*xmult,2*linespacing,@CTSCROLLHORIZ,volumescrollbar
SETSCROLLRANGE win,volumescrollbar,16,127
SETSCROLLPOS win,volumescrollbar,64
CONTROL win,@SCROLLBAR,"",100*xmult,38*linespacing,150*xmult,2*linespacing,@CTSCROLLHORIZ,temposcrollbar
SETSCROLLRANGE win,temposcrollbar,16,100
SETSCROLLPOS win,temposcrollbar,40
'Create buttons
CONTROL win,@BUTTON,"Add note after selected",60*xmult,19*linespacing,22*linespacing,20*ymult,0,insertbutton
CONTROL win,@BUTTON,"Add rest after selected",60*xmult,22*linespacing,22*linespacing,20*ymult,0,insertpausebutton
CONTROL win,@BUTTON,"Delete selected",60*xmult,25*linespacing,220*xmult,2*linespacing,0,deletebutton
CONTROL win,@BUTTON,"Play all notes",40*xmult,29*linespacing,260*xmult,2*linespacing,0,playallbutton
CONTROL win,@BUTTON,"Play visible notes from selected one",40*xmult,32*linespacing,260*xmult,2*linespacing,0,playvisiblenotesbutton
CONTROL win,@BUTTON,"Stop music",40*xmult,35*linespacing,260*xmult,2*linespacing,0,stopmusicbutton
'Create and fill drop down list with midi instrument names
CONTROL win,@COMBOBOX,"",450*xmult,20.5*linespacing,180*xmult,20*linespacing,@CTCOMBODROPLIST|@VSCROLL,instrumentcombobox
FOR instrument=0 TO 127
ADDSTRING win,instrumentcombobox,instrumentname$[instrument]
NEXT instrument
SETSELECTED win,instrumentcombobox,noteinstrument[0]
'Create static text boxes
CONTROL win,@STATIC,"Name",370*xmult,31*linespacing,50*xmult,2*linespacing,0,textbox1
SETCONTROLCOLOR win,textbox1,0,0xFFFFFF
CONTROL win,@STATIC,"Selected note parameters",420*xmult,18*linespacing,250*xmult,2*linespacing,0,textbox2
SETCONTROLCOLOR win,textbox2,0,0xFFFFFF
CONTROL win,@STATIC,"Instrument",370*xmult,20.5*linespacing,80*xmult,2*linespacing,0,textbox3
SETCONTROLCOLOR win,textbox3,0,0xFFFFFF
CONTROL win,@STATIC,"Volume",370*xmult,23*linespacing,70*xmult,2*linespacing,0,textbox4
SETCONTROLCOLOR win,textbox4,0,0xFFFFFF
CONTROL win,@STATIC,"Overall tempo",5*xmult,38.2*linespacing,95*xmult,2*linespacing,0,textbox6
SETCONTROLCOLOR win,textbox6,0,0xFFFFFF
text$="Use up/down arrow keys to change pitch of selected note."+CHR$(13)+CHR$(10)
text$=text$+"Use left/right arrow keys to change length of selected note."+CHR$(13)+CHR$(10)+"Click on a note to select it."
text$=text$+"Unselected single notes are black while unselected chords are dark blue."
CONTROL win,@EDIT,text$,340*xmult,33.5*linespacing,290*xmult,7.5*linespacing,@VSCROLL|@CTEDITMULTI,textbox5
SETCONTROLCOLOR win,textbox5,0,0xFFFFFF
'Create chord/note radio buttons
radiobuttonheight=2*linespacing
CONTROL win,@RADIOBUTTON,"Single note",370*xmult,26*linespacing,100*xmult,radiobuttonheight,@TABSTOP|@GROUP,noteradiobutton
CONTROL win,@RADIOBUTTON,"Chord",370*xmult,26*linespacing+radiobuttonheight,100*xmult,2*linespacing,@TABSTOP,chordradiobutton
SETSTATE win,noteradiobutton,1
RETURN
ENDSUB
SUB drawnotes
DEF colour,notetop,noteleft,lastnote,notewidth:INT
DEF currentnotename$:STRING
lastnote=numberofnoteatleft+15
IF lastnote>numberofnotes-1 THEN lastnote=numberofnotes-1
selectednotevisible=0
FOR notenumber=numberofnoteatleft TO lastnote
'Set drawing colour to red if selected,black if single note and blue if chord
IF notenumber=selectednote
colour=255:selectednotevisible=1
ELSE
IF noteisachord[notenumber]
colour=blue
ELSE
colour=0
ENDIF
ENDIF
'Calculate y posn for top of note
currentnotename$=notename$[notepitch[notenumber]%12]
notetop=14.5*linespacing-3.5*linespacing*(FLOOR(notepitch[notenumber]/12))-.5*linespacing*(ASC(currentnotename$)-67)+1
IF currentnotename$="A" | currentnotename$="A#" | currentnotename$="B"
notetop=notetop-3.5*linespacing
ENDIF
notewidth=notewidthinpixels()
noteleft=(notenumber-numberofnoteatleft)*maxnotewidth+maxnotewidth/2.0-notewidth/2
IF noteisarest[notenumber]
'Draw a rest
RECT win,noteleft,notetop,notewidth,linespacing-1,colour,colour
ELSE
'Draw a note
ELLIPSE win,noteleft,notetop,notewidth,linespacing-1,colour,colour
IF LEN(currentnotename$)>1
'Note is a sharp.Draw #
MOVE win,.6*maxnotewidth+(notenumber-numberofnoteatleft)*maxnotewidth,notetop-15*ymult
FRONTPEN win,colour:DRAWMODE win,@TRANSPARENT
PRINT win,"#"
FRONTPEN win,0:DRAWMODE win,@OPAQUE
ENDIF
ENDIF
NEXT notenumber
'Print selected note name
IF noteisarest[selectednote]
'Rest selected
MOVE win,530*xmult,31*linespacing:PRINT win,"Rest"
ELSE
'Note selected
currentnotename$=notename$[notepitch[selectednote]%12]
MOVE win,530*xmult,31*linespacing:PRINT win,currentnotename$," "
GOSUB drawnotesymbol
ENDIF
RETURN
ENDSUB
SUB createmidi$(firstnote:INT,lastnote:INT)
DEF secondnotepitch,thirdnotepitch,lastoctave,lastduration,lastinstrument,lastvolume:INT
DEF lasttempo,duration,tempo:INT
lastoctave=-1:lastduration=-1:lastinstrument=-1:lastvolume=-1:lasttempo=-1
midi$="N0 "
FOR notenumber=firstnote TO lastnote
IF noteduration[notenumber]%3=0
'Not a dotted note
tempo=uppertempo:duration=(noteduration[notenumber]*2)/3
ELSE
'Dotted note
tempo=lowertempo:duration=noteduration[notenumber]
ENDIF
IF noteisarest[notenumber]
'Rest
midi$=midi$+" R"+LTRIM$(STR$(duration))+" "
IF 4+FLOOR(notepitch[notenumber]/12)<>lastoctave
midi$=midi$+"O"+LTRIM$(STR$(4+(FLOOR(notepitch[notenumber]/12))))+" "
lastoctave=4+FLOOR(notepitch[notenumber]/12)
ENDIF
ELSE
'Note
IF 4+FLOOR(notepitch[notenumber]/12)<>lastoctave
'Octave for this note is different to that for last one
midi$=midi$+"O"+LTRIM$(STR$(4+(FLOOR(notepitch[notenumber]/12))))+" "
lastoctave=4+FLOOR(notepitch[notenumber]/12)
ENDIF
IF notevolume[notenumber]<>lastvolume
'Volume for this note is different to that for last one
midi$=midi$+"V"+LTRIM$(STR$(notevolume[notenumber]))+" "
lastvolume=notevolume[notenumber]
ENDIF
IF noteinstrument[notenumber]<>lastinstrument
'Instrument for this note is different to that for last one
midi$=midi$+"I"+LTRIM$(STR$(noteinstrument[notenumber]))+" "
lastinstrument=noteinstrument[notenumber]
ENDIF
'If note is a chord add * to denote start of chord
IF noteisachord[notenumber] THEN midi$=midi$+"*"
IF tempo<>lasttempo
midi$=midi$+" T"+LTRIM$(STR$(tempo))+" "
lasttempo=tempo
ENDIF
'Add name of note to midi$
midi$=midi$+notename$[notepitch[notenumber]%12]
IF noteduration[notenumber]<>lastduration
'Duration for this note is different to that for last one
midi$=midi$+LTRIM$(STR$(duration))+" "
lastduration=noteduration[notenumber]
ENDIF
IF noteisachord[notenumber]
'Create two more notes to make chord
secondnotepitch=notepitch[notenumber]+4
IF 4+FLOOR(secondnotepitch/12)<>lastoctave
midi$=midi$+"O"+LTRIM$(STR$(4+FLOOR(secondnotepitch/12)))
lastoctave=4+FLOOR(secondnotepitch/12)
ENDIF
midi$=midi$+notename$[secondnotepitch%12]
thirdnotepitch=notepitch[notenumber]+7
IF 4+FLOOR(thirdnotepitch/12)<>lastoctave
midi$=midi$+"O"+LTRIM$(STR$(4+FLOOR(thirdnotepitch/12)))
lastoctave=4+FLOOR(thirdnotepitch/12)
ENDIF
midi$=midi$+notename$[thirdnotepitch%12]+";"
ENDIF
ENDIF
NEXT notenumber
RETURN
ENDSUB
SUB savemidi$assubroutine
'Saves the midi$ as a subroutine
DEF midi$codeline,numberofmidi$codelines:INT
DEF n:FLOAT
DEF subroutinefilename,textline$:STRING
DEF subroutinefile:FILE
subroutinefilename=FILEREQUEST("Choose name for midi$ subroutine",win,0,"midi$ files (*.midi$.txt)|*.midi$.txt||",".midi$.txt",0,GETSTARTPATH)
IF subroutinefilename<>""
GOSUB createmidi$(0,numberofnotes-1)
OPENFILE (subroutinefile,subroutinefilename,"W")
WRITE subroutinefile,"SUB createandplaymidi$(),POINTER"
WRITE subroutinefile," DEF midi$["+LTRIM$(STR$(LEN(midi$)+1))+"]:ISTRING"
WRITE subroutinefile," DEF pointertomidi$:POINTER"
n=LEN(midi$):n=n/64
numberofmidi$codelines=CEIL(n)
FOR midi$codeline=0 TO numberofmidi$codelines-1
textline$=" midi$="
IF midi$codeline<>0 THEN textline$=textline$+"midi$+"
textline$=textline$+CHR$(34)+RTRIM$(LTRIM$(MID$(midi$,midi$codeline*64+1,64)))+CHR$(34)
WRITE subroutinefile,RTRIM$(textline$)
NEXT midi$codeline
WRITE subroutinefile," pointertomidi$=PLAYMIDI$(midi$,TRUE)"
WRITE subroutinefile,"RETURN pointertomidi$"
WRITE subroutinefile,"ENDSUB"
CLOSEFILE subroutinefile
ENDIF
RETURN
ENDSUB
SUB savemusic
DEF savefilename,textline$:STRING
DEF savefile:FILE
savefilename=FILEREQUEST("Choose name for music to save",win,0,"music files (*.music.txt)|*.music.txt||",".music.txt",0,GETSTARTPATH)
IF savefilename<>""
OPENFILE (savefile,savefilename,"W")
textline$=LTRIM$(STR$(numberofnotes))+" note"
IF numberofnotes>1 THEN textline$=textline$+"s"
WRITE savefile,textline$
WRITE savefile,LTRIM$(STR$(uppertempo))+" tempo"
FOR notenumber=0 TO numberofnotes-1
textline$=LTRIM$(STR$(notepitch[notenumber]))+","
textline$=textline$+LTRIM$(STR$(noteduration[notenumber]))+","
textline$=textline$+LTRIM$(STR$(notevolume[notenumber]))+","
textline$=textline$+LTRIM$(STR$(noteinstrument[notenumber]))+","
textline$=textline$+LTRIM$(STR$(noteisarest[notenumber]))+","
textline$=textline$+LTRIM$(STR$(noteisachord[notenumber]))
WRITE savefile,textline$
NEXT notenumber
CLOSEFILE savefile
ENDIF
RETURN
ENDSUB
SUB loadmusic
DEF loadfilename,textline$:STRING
DEF loadfile:FILE
loadfilename=FILEREQUEST("Choose name for music to load",win,1,"music files (*.music.txt)|*.music.txt||","",0,GETSTARTPATH)
IF loadfilename<>""
OPENFILE (loadfile,loadfilename,"R")
READ loadfile,textline$
IF VAL(textline$)=0 | INSTR(textline$,"note")=0
MESSAGEBOX win,"Not a valid music file","Oops"
ELSE
numberofnotes=VAL(textline$)
READ loadfile,textline$
uppertempo=VAL(textline$)
lowertempo=(uppertempo*2)/3
SETSCROLLPOS win,temposcrollbar,lowertempo/2
FOR notenumber=0 TO numberofnotes-1
READ loadfile,textline$
notepitch[notenumber]=VAL(textline$)
textline$=MID$(textline$,INSTR(textline$,",")+1)
noteduration[notenumber]=VAL(textline$)
textline$=MID$(textline$,INSTR(textline$,",")+1)
notevolume[notenumber]=VAL(textline$)
textline$=MID$(textline$,INSTR(textline$,",")+1)
noteinstrument[notenumber]=VAL(textline$)
textline$=MID$(textline$,INSTR(textline$,",")+1)
noteisarest[notenumber]=VAL(textline$)
textline$=MID$(textline$,INSTR(textline$,",")+1)
noteisachord[notenumber]=VAL(textline$)
NEXT notenumber
selectednote=0:numberofnoteatleft=0
IF noteisarest[0]=0
SETSELECTED win,instrumentcombobox,noteinstrument[0]
SETSCROLLPOS win,volumescrollbar,notevolume[0]
ENDIF
laststaffscrollbarposn=0
SETSCROLLRANGE win,staffscrollbar,0,numberofnotes-1
SETSCROLLPOS win,staffscrollbar,0
SETSTATE win,chordradiobutton,noteisachord[0]
SETSTATE win,noteradiobutton,1-noteisachord[0]
'Redraw stuff
GOSUB drawstaff
GOSUB drawnotes
GOSUB enableordisablecontrols
ENDIF
CLOSEFILE loadfile
ENDIF
RETURN
ENDSUB
SUB scrollbarshandler
IF @CONTROLID=staffscrollbar
select @CODE
case @SBTHUMBPOS:case @SBTHUMBTRACK
setscrollpos win,staffscrollbar,getthumbpos(win,staffscrollbar)
case @SBLINELEFT
setscrollpos win,staffscrollbar,(getscrollpos(win,staffscrollbar)-1)
case @SBLINERIGHT
setscrollpos win,staffscrollbar,(getscrollpos(win,staffscrollbar)+1)
case @SBPAGELEFT
setscrollpos win,staffscrollbar,(getscrollpos(win,staffscrollbar)-10)
case @SBPAGERIGHT
setscrollpos win,staffscrollbar,(getscrollpos(win,staffscrollbar)+10)
endselect
numberofnoteatleft=getscrollpos(win,staffscrollbar)
IF numberofnoteatleft<>laststaffscrollbarposn
'Scroll bar has moved.Redraw staff & notes
GOSUB drawstaff
GOSUB drawnotes
GOSUB enableordisablecontrols
laststaffscrollbarposn=numberofnoteatleft
ENDIF
ENDIF
IF @CONTROLID=volumescrollbar
select @CODE
case @SBTHUMBPOS:case @SBTHUMBTRACK
setscrollpos win,volumescrollbar,getthumbpos(win,volumescrollbar)
case @SBLINELEFT
setscrollpos win,volumescrollbar,(getscrollpos(win,volumescrollbar)-1)
case @SBLINERIGHT
setscrollpos win,volumescrollbar,(getscrollpos(win,volumescrollbar)+1)
case @SBPAGELEFT
setscrollpos win,volumescrollbar,(getscrollpos(win,volumescrollbar)-10)
case @SBPAGERIGHT
setscrollpos win,volumescrollbar,(getscrollpos(win,volumescrollbar)+10)
endselect
notevolume[selectednote]=getscrollpos(win,volumescrollbar)
ENDIF
IF @CONTROLID=temposcrollbar
select @CODE
case @SBTHUMBPOS:case @SBTHUMBTRACK
setscrollpos win,temposcrollbar,getthumbpos(win,temposcrollbar)
case @SBLINELEFT
setscrollpos win,temposcrollbar,(getscrollpos(win,temposcrollbar)-1)
case @SBLINERIGHT
setscrollpos win,temposcrollbar,(getscrollpos(win,temposcrollbar)+1)
case @SBPAGELEFT
setscrollpos win,temposcrollbar,(getscrollpos(win,temposcrollbar)-10)
case @SBPAGERIGHT
setscrollpos win,temposcrollbar,(getscrollpos(win,temposcrollbar)+10)
endselect
lowertempo=2*getscrollpos(win,temposcrollbar)
uppertempo=3*getscrollpos(win,temposcrollbar)
ENDIF
RETURN
ENDSUB
SUB insertnoteorrest
ENABLECONTROL win,deletebutton,1
IF selectednote<>numberofnotes-1
'Selected note not the last one
'Copy note data to following note for notes after selected one
FOR notenumber=numberofnotes-1 TO selectednote+1 STEP -1
notevolume[notenumber+1]=notevolume[notenumber]
notepitch[notenumber+1]=notepitch[notenumber]
noteinstrument[notenumber+1]=noteinstrument[notenumber]
noteduration[notenumber+1]=noteduration[notenumber]
noteisarest[notenumber+1]=noteisarest[notenumber]
noteisachord[notenumber+1]=noteisachord[notenumber]
NEXT notenumber
ENDIF
'Create data for new note/rest
IF @CONTROLID=insertbutton
'Add a note
noteisarest[selectednote+1]=0
'Set radio buttons
SETSTATE win,chordradiobutton,noteisachord[selectednote]
SETSTATE win,noteradiobutton,1-noteisachord[selectednote]
ELSE
'Add a rest
noteisarest[selectednote+1]=1
ENDIF
notevolume[selectednote+1]=notevolume[selectednote]
notepitch[selectednote+1]=notepitch[selectednote]
noteduration[selectednote+1]=noteduration[selectednote]
noteinstrument[selectednote+1]=noteinstrument[selectednote]
noteisachord[selectednote+1]=noteisachord[selectednote]
numberofnotes=numberofnotes+1
'Alter scrollbar range
SETSCROLLRANGE win,staffscrollbar,0,numberofnotes-1
selectednote=selectednote+1
'Move scrollbar if new note off right of window
IF getscrollpos(win,staffscrollbar)+15<selectednote
SETSCROLLPOS(win,staffscrollbar,selectednote-15)
numberofnoteatleft=selectednote-15
ENDIF
GOSUB drawstaff
GOSUB drawnotes
GOSUB enableordisablecontrols
RETURN
ENDSUB
SUB enableordisablecontrols
IF selectednotevisible
ENABLECONTROL win,insertbutton,1
ENABLECONTROL win,insertpausebutton,1
ENABLECONTROL win,playvisiblenotesbutton,1
ELSE
ENABLECONTROL win,insertbutton,0
ENABLECONTROL win,insertpausebutton,0
ENABLECONTROL win,playvisiblenotesbutton,0
ENDIF
IF selectednotevisible & numberofnotes>1
ENABLECONTROL win,deletebutton,1
ELSE
ENABLECONTROL win,deletebutton,0
ENDIF
IF selectednotevisible & noteisarest[selectednote]=0
'Enable note parameter controls
ENABLECONTROL win,instrumentcombobox,1
ENABLECONTROL win,volumescrollbar,1
ENABLECONTROL win,noteradiobutton,1
ENABLECONTROL win,chordradiobutton,1
ELSE
'Disable note parameter controls
ENABLECONTROL win,instrumentcombobox,0
ENABLECONTROL win,volumescrollbar,0
ENABLECONTROL win,noteradiobutton,0
ENABLECONTROL win,chordradiobutton,0
ENDIF
IF numberofnotes<17 & numberofnoteatleft=0
ENABLECONTROL win,staffscrollbar,0
ELSE
ENABLECONTROL win,staffscrollbar,1
ENDIF
RETURN
ENDSUB
SUB deletenoteorrest
IF selectednote<>numberofnotes
'Selected note is not the last note
'Copy note data to previous note for notes after deleted one
FOR notenumber=selectednote TO numberofnotes-2
notepitch[notenumber]=notepitch[notenumber+1]
noteduration[notenumber]=noteduration[notenumber+1]
noteinstrument[notenumber]=noteinstrument[notenumber+1]
notevolume[notenumber]=notevolume[notenumber+1]
noteisarest[notenumber]=noteisarest[notenumber+1]
noteisachord[notenumber]=noteisachord[notenumber+1]
NEXT notenumber
ENDIF
numberofnotes=numberofnotes-1
'Alter scrollbar range
SETSCROLLRANGE win,staffscrollbar,0,numberofnotes-1
selectednote=selectednote-1
IF selectednote=-1 THEN selectednote=0
'Check if deleted last note at left of window
IF numberofnoteatleft>numberofnotes-1
numberofnoteatleft=numberofnotes-1
ENDIF
'Set radio buttons
SETSTATE win,chordradiobutton,noteisachord[selectednote]
SETSTATE win,noteradiobutton,1-noteisachord[selectednote]
GOSUB drawstaff
GOSUB drawnotes
GOSUB enableordisablecontrols
RETURN
ENDSUB
SUB eraseselectednote
DEF currentnotename$:STRING
DEF notetop,left:INT
'Calculate y posn for top of note
currentnotename$=notename$[notepitch[selectednote]%12]
notetop=14.5*linespacing-3.5*linespacing*(FLOOR(notepitch[selectednote]/12))-.5*linespacing*(ASC(currentnotename$)-67)+1
IF currentnotename$="A" | currentnotename$="A#" | currentnotename$="B"
notetop=notetop-3.5*linespacing
ENDIF
left=maxnotewidth*(selectednote-numberofnoteatleft)
'Erase note/rest
RECT win,left,notetop,maxnotewidth,linespacing,0xFFFFFF,0xFFFFFF
IF noteisarest[selectednote]=0
'Note is not a rest.Check if it is a sharp
IF LEN(currentnotename$)>1
'Note is a sharp.Erase #
MOVE win,.6*maxnotewidth+(selectednote-numberofnoteatleft)*maxnotewidth,notetop-15*ymult
FRONTPEN win,0xFFFFFF:DRAWMODE win,@TRANSPARENT
PRINT win,"#"
FRONTPEN win,0:DRAWMODE win,@OPAQUE
ENDIF
ENDIF
'Redraw staff lines
LINE win,left,3*linespacing,left+maxnotewidth,3*linespacing
LINE win,left,4*linespacing,left+maxnotewidth,4*linespacing
LINE win,left,5*linespacing,left+maxnotewidth,5*linespacing
LINE win,left,6*linespacing,left+maxnotewidth,6*linespacing
LINE win,left,7*linespacing,left+maxnotewidth,7*linespacing
LINE win,left,9*linespacing,left+maxnotewidth,9*linespacing
LINE win,left,10*linespacing,left+maxnotewidth,10*linespacing
LINE win,left,11*linespacing,left+maxnotewidth,11*linespacing
LINE win,left,12*linespacing,left+maxnotewidth,12*linespacing
LINE win,left,13*linespacing,left+maxnotewidth,13*linespacing
RETURN
ENDSUB
SUB drawnotesymbol
DEF noteiconx,noteicony,noteiconradius,duration,numberoflines,linenumber:INT
noteiconx=500*xmult:noteicony=32*linespacing
noteiconradius=4*xmult:duration=noteduration[selectednote]
RECT win,noteiconx-5*xmult,noteicony-20*ymult,30*xmult,30*ymult,0xFFFFFF,0xFFFFFF
IF duration<8
CIRCLE win,noteiconx,noteicony,noteiconradius,0,0xFFFFFF
ELSE
CIRCLE win,noteiconx,noteicony,noteiconradius,0,0
ENDIF
IF duration>=4
LINE win,noteiconx+noteiconradius-1,noteicony,noteiconx+noteiconradius-1,noteicony-20*ymult
ENDIF
IF duration%3<>0
CIRCLE win,noteiconx+noteiconradius+2*xmult,noteicony+noteiconradius,xmult,0,0
ENDIF
IF duration>=16
numberoflines=1
IF duration>=32 THEN numberoflines=2
IF duration>=64 THEN numberoflines=3
FOR linenumber=0 TO numberoflines-1
LINE win,noteiconx+noteiconradius,noteicony-(20-3*linenumber)*ymult,noteiconx+noteiconradius+5*xmult,noteicony-(17-3*linenumber)*ymult
NEXT linenumber
ENDIF
RETURN
ENDSUB
SUB notewidthinpixels(),INT
DEF widthofnote:INT
SELECT noteduration[notenumber]
CASE 2:widthofnote=maxnotewidth
CASE 3:widthofnote=maxnotewidth*31/40
CASE 4:widthofnote=maxnotewidth*25/40
CASE 6:widthofnote=maxnotewidth*20/40
CASE 8:widthofnote=maxnotewidth*16/40
CASE 12:widthofnote=maxnotewidth*13/40
CASE 16:widthofnote=maxnotewidth*11/40
CASE 24:widthofnote=maxnotewidth*9/40
CASE 32:widthofnote=maxnotewidth*7/40
CASE 48:widthofnote=maxnotewidth*5/40
CASE 64:widthofnote=maxnotewidth*4/40
CASE 96:widthofnote=maxnotewidth*3/40
ENDSELECT
RETURN widthofnote
ENDSUB
SUB fillinstrumentarray
instrumentname$="Acoustic grand","Bright acoustic","Electric grand","Honky tonk","Electric piano 1","Electric piano 2"
instrumentname$[6]="Harpsichord","Clav","Celesta","Glockenspiel","Music box","Vibraphone"
instrumentname$[12]="Marimba","Xylophone","Tubular bells","Dulcima","Drawbar organ","Percussive organ"
instrumentname$[18]="Rock organ","Church organ","Reed organ","Accordion","Harmonica","Tango accordion"
instrumentname$[24]="Acoustic guitar(nylon)","Acoustic guitar(steel)","Electric guitar(jazz)","Electric guitar(clean)","Electric guitar(muted)"
instrumentname$[29]="Overdriven guitar","Distortion guitar","Guitar harmonics","Acoustic bass","Electric bass(finger)","Electric bass(pick)"
instrumentname$[35]="Fretless bass","Slap bass 1","Slap bass 2","Synth bass 1","Synth bass 2","Violin"
instrumentname$[41]="Viola","Cello","Contrabass","Tremelo strings","Pizzicato strings","Orchestral strings"
instrumentname$[47]="Timpani","String ensemble 1","String ensemble 2","Synth strings 1","Synth strings 2","Choir aahs"
instrumentname$[53]="Voice oohs","Synth voice","Orchestra hit","Trumpet","Trombone","Tuba"
instrumentname$[59]="Muted trumpet","French horn","Brass section","Synth brass 1","Synth brass 2","Soprano sax"
instrumentname$[65]="Alto sax","Tenor sax","Baritone sax","Oboe","English horn","Bassoon"
instrumentname$[71]="Clarinet","Piccolo","Flute","Recorder","Pan flute","Blown bottle"
instrumentname$[77]="Skakuhachi","Whistle","Occarina","Lead 1(square)","Lead 2(sawtooth)","Lead 3(calliope)"
instrumentname$[83]="Lead 4(chiff)","Lead 5(charang)","Lead 6(voice)","Lead 7(fifths)","Lead 8(bass&lead)","Pad 1(new age)"
instrumentname$[89]="Pad 2(warm)","Pad 3(polysynth)","Pad 4(choir)","Pad 5(bowed)","Pad 6(metallic)","Pad 7(halo)"
instrumentname$[95]="Pad 8(sweep)","FX 1(rain)","FX 2(soundtrack)","FX 3(crystal)","FX 4(atmosphere)","FX 5(brightness)"
instrumentname$[101]="FX 6(goblins)","FX 7(echoes)","FX 8(sci-fi)","Sitar","Banjo","Shamisen"
instrumentname$[107]="Koto","Kalimba","Bagpipe","Fiddle","Shanai","Tinkle bell"
instrumentname$[113]="Agogo","Steel drums","Woodblock","Taiko drum","Melodic tom","Synth drum"
instrumentname$[119]="Reverse cymbal","Guitar fret noise","Breath noise","Seashore","Bird tweet","Telephone ring"
instrumentname$[125]="Helicopter","Applause","Gun shot"
RETURN
ENDSUB
/*
The code below shows the use of the subroutines created.
Code:
AUTODEFINE "OFF"
DEF win:WINDOW
DEF run:INT
DEF music:POINTER
OPENWINDOW win,0, 0, 640, 480,@MINBOX, 0, "MIDI$ subroutine player", &main
CONTROL win,@BUTTON,"Play",100,200,100,20,0,1
CONTROL win,@BUTTON,"Stop",300,200,100,20,0,2
run = 1: WAITUNTIL run = 0: CLOSEWINDOW win: END
SUB main
SELECT @CLASS
CASE @IDCLOSEWINDOW
run = 0
CASE @IDCONTROL
IF @CONTROLID=1
music=createandplaymidi$()
ELSE
STOPMIDI$ music
ENDIF
ENDSELECT
RETURN
ENDSUB
SUB createandplaymidi$(),POINTER
DEF midi$[97]:ISTRING
DEF pointertomidi$:POINTER
midi$="N0 O5 V64 I0 * T154 A2 O6C#E;O5 A4 BO6 *C2 EG;C4 C#*D2 F#A;D4 D#"
midi$=midi$+"V127 *E2 G#B;E4 DCO5 B*A2 O6C#E;"
pointertomidi$=PLAYMIDI$(midi$,TRUE)
RETURN pointertomidi$
ENDSUB
*/
Thank you !
I think it is easy for Paul Turley to add to playmidi$ the possibility to program several independent musical lines.
This is done in a java module called JFugue : each channel starts simultaneously.
Java is too complicated for me. The EBasic's PLAYMIDI$ commands are very useful and the language is perfect !
This add is valuable for me !
I omitted to specify that i am ready to pay for this improvement, which is very important for me.
ebasic is so native in so many excellent ways, but im dissapointed :'( in music support. :'( .WAV is bloated, and midi is difficult to use to full sound card capability. :'(. correct me if im rong.
oh well.
maybe, i can use several .COM threads to emulate different channels? :)