EUG PD


Synchronised Music/Words

 
Published in EUG #54

The Electron's ability to play music whilst it is performing other actions is very useful, but it can make it difficult to synchronise the music with the appearance of words on the screen. Brian Boyde-Shaw gives the low down on putting words to your music. Note that the demonstration programs are stored in the U directory in the file "RHYME".

Trying to print out the words of a song while it plays through your Electron's speaker is no easy task. Because of the way in which sound commands are handled by the Electron, there is a tendency for the words and the music to become totally separated.

In the program in this article the sound is broken down into single notes, and combined with text - words or part words - so that the text is being written on the screen at the same time as you hear the corresponding note.

The aim of the program is to play a simple nursery rhyme, one note at at a time as the words are displayed on the screen.

The nursery rhyme we have chosen here is 'One, two, three, four, five'. The methods used, however, can easily be adapted to any musical piece.

Let's first look at a procedure to carry out this routine. As there are twenty four notes in the rhyme, we can conveniently read in the word or part word, the pitch of the note and the note's duration, from DATA statements.

      4000DATA one,88,8,two,88,8,three,80,4,four,72,4,five,72,8
      4010DATA once,80,4,I,88,4,caught,92,4,a,100,4,fish,100,4,a,92,4,li
   ve,92,8
      4020DATA six,92,8,seven,92,8,eight,88,4,nine,80,4,ten,80,8      
      4030DATA then,72,4,I,68,4,let,60,4,it,68,4,go,80,4,a,72,4,gain,72,
   8

We can then print the word in the centre of the screen, play the note, and clear the screen. Then do it all again until we reach the end of the rhyme.

      1000DEF PROCwords   
      1010FOR R=1 TO 24
      1020READ WORD$,PITCH,DUR
      1030PRINT TAB(15,15)WORD$
      1040SOUND 1,-15,PITCH,DUR
      1050T%=TIME:REPEATUNTIL TIME>T%+5*DUR
      1060CLS
      1070NEXT R
      1080ENDPROC

This procedure is designed to work in Mode 4, but could be adapted for any other mode, providing that the parameters of the TAB statement (line 1030) are adjusted according to the appropriate line width.

Line 1050 will need some explanation. We need a delay loop to display the word on the screen for the time that the note sounds. If we allowed the program to continue without this delay loop, the screen would clear, then the next word would be printed, and so on, and so on, until all the words had been displayed, but the music would have hardly started. On the Electron, sounds are placed in a queue ready for processing. This takes up very little of the computer's time. Drawing and printing onto the screen, however, does take a while, so the two can get out of step. You can try this procedure without this line to see (and hear) this effect.

We must delay the printing of the 'next' word until the note has finished playing, and line 1050 does this. As the duration of the note (DUR from the data statements) is in twentieths of a second - for the SOUND statement - this delay must be converted to the units of hundredth of a second - the units of TIME. Line 1050 makes the conversion and delays the next word from appearing on the screen.

For the first note, for example, the SOUND statement requires, a duration value of 8. The delay loop, however, requires a value of 40 hundredth of a second. Line 1050 makes the conversion with the formula:

      DELAY=DUR*5

You could, of course, put both timing codes separately into the data statements, but this would mean more effort typing in the music, and be wasteful of memory into the bargain.

We now have a simple procedure to show how words and music can be connected, but there are other more exciting ways of doing this. We can allow the text to build up on the screen, word by word, until the complete verse is displayed.

The same data statements can't be used because we need punctuation on the screen now that we are to have the whole verse visible at once. When we include punctuation in data we have to wrap the data in inverted commas. Another snag is that we have to provide some means of telling the computer to start a new line for each line of the verse and that "alive" and "again" are two notes each but only one word. We need to introduce a coding system into our data.

      4050DATA "*One,",88,8,"two,",88,8,"three,",80,4,"four,",72,4,"five
   ,",72,8
      4060DATA "*Once",80,4,"I",88,4,"caught",92,4,"a",100,4,"fish",100,
   4,"a",92,4,"-live",92,8
      4070DATA "*Six,",92,8,"seven,",92,8,"eight,",88,4,"nine,",80,4,"te
   n,",80,8
      4080DATA "*Then",72,4,"I",68,4,"let",60,4,"it",68,4,"go",80,4,"a",
   72,4,"-gain",72,8

The asterisk signals that a new line is to be printed and the dash that this word is to be joined to onto the previous one. This is the procedure that decodes these characters and prints out the whole verse in time to the music:

      2000DEF PROClines  
      2010PRINT '''
      2020FOR R=1 TO 24
      2030READ WORD$,PITCH,DUR 
      2040CONTROL$=LEFT$(WORD$,1)
      2050IF CONTROL$<>"-" AND CONTROL$<>"*" THEN 2090
      2060WORD$=RIGHT$(WORD$,LEN(WORD$)-1)
      2070IF CONTROL$="*" THEN PRINT ''
      2080IF CONTROL$="-" THEN VDU 8
      2090PRINT WORD$;" ";
      2100SOUND 1,-15,PITCH,DUR  
      2110T%=TIME:REPEATUNTIL TIME>T%+5*DUR
      2120NEXT R   
      2130PRINT TAB(5,20)"PRESS SPACE BAR TO CONTINUE."
      2140REPEAT UNTIL GET=32
      2150ENDPROC

This second procedure is for Mode 4 as well. Again, it could easily be converted to other modes by altering the TAB statements and the new line control characters in the data.

Line 2040 removes the control character from the lyric and checks to see if it is in fact a control character. If it isn't then the note is sounded, the word is printed onto the screen, and a delay loop used as before. This time the word is printed to follow directly on from the last one, without clearing the screen.

The lines 2070 and 2080 act according to the control character found. If an asterisk is found a new line is printed before the word. If a dash is there then the cursor is moved back one position, using VDU 8, to join the word to the last one printed.

As we are writing the complete rhyme now, clearing the screen as soon as the last word is printed isn't a good idea. A chance is given for the user to read the verse with a pause in lines 2130 and 2140. GET reads the keyboard and returns the ASCII code for the key being pressed. The space bar has an ASCII code of 32.

We now have two procedures and two sets of data, one to print out the words only, and one to print out the verse, line by line. So now let's put them into a complete program:

        10MODE 4
        20VDU23;8202;0;0;0;
        30REPEAT
        40PROCcontinue
        50UNTIL FALSE
        60END
        70:
      3000DEF PROCcontinue   
      3010CLS
      3020VDU19,0,4;0;19,1,3;0;
      3030PRINT TAB(5,5)"Press W to play the words."
      3040PRINT TAB(7,7)"or P to play the poem."
      3050A$=GET$
      3060A=INSTR("WwPp",A$)
      3070IF A=0 THEN 3050
      3080CLS
      3090IF A<3 RESTORE 4000:PROCwords
      3100IF A>2 RESTORE 4050:PROClines
      3110ENDPROC

First Mode 4 is set up without a cursor and then the procedure, PROCcontinue is called again and again until you tire of the rhyme.

PROCcontinue simply clears the screen and redefines the foreground and background colours to prettier variants, then offers you the choice between seeing the rhyme one word at a time or building up on the screen. Your answer is compared in line 3060 with the possible legitimate answers. Whichever you pick, the right data statements are selected with the RESTORE command and the requisite procedure called.

When you have heard 'One, two, three, four, five' to your heart's content, you may like to enter your own music. You can put any tune in as data. Each note should be entered as: word printed, pitch, and duration. Don't forget to change the total number of notes (24 in the example) in lines 1010 and 2020.

Brian Boyde-Shaw, ELBUG 2.1
Reproduced EUG #54

Brian Boyle-Shaw