consp06@bingsuns.cc.binghamton.edu (Robert Konigsberg) (10/01/90)
I'm working on a program that I would like to include background music. I know there is code out there that to do this. After all, Turbo Basic has the "PLAY" statement. I started writing my own, but it just ain't workin out as neet as I wanted it to be, If anyone has any code at all whatsoever (it could be in C, too) compiled or not, (preferable not compiled) please let me know.. Or, feel free to discuss the difficulties here. What I mean is that ANY information would be enlightening -Rob Konigsberg
KRW1@Lehigh (10/03/90)
Here's a sample program for doing background music in Turbo Pascal. It could be turned into a general-purpose unit without too much difficulty. -- Kevin ------------------------------------------------------------------- program backplay; {Sample program demonstrating how background music may be played in a running Turbo Pascal program. The routine getevents should be replaced with something that loads the actual song. Events consist of a note frequency specification (look up value in period array; item 0 is no sound) and a duration in system ticks (.0549 sec). Background playing is driven by the 1C user timer interrupt (18.2 int/sec). Higher resolution can be accomplished, but is more difficult. The base timer interrupt (08) can be changed to a different rate in a manner similar to programming the speaker timer, but this is dangerous, since other processes may be depending on the correct rate. It is also possible to use the DOS background/idle interrupt, but that is unpredictable and must be used in conjunction with a timer interrupt for synchronization. It is also possible to use a temporary stack in the interrupt routine if more sophisticated processing is required. Contact the author for additional information. Kevin Weiner krw1@ns.cc.lehigh.edu } {$r-,s-} uses dos, crt; const maxevents = 100; {Max size of event list} {Note frequencies in cycles per second corresponding to MIDI notes 1-127 (C#-2 to G8). Middle C is note 60. Frequencies are converted to .84 microsecond counter ticks in array period in the initialization routine for actual timing use.} notefreq: array [0..127] of integer = ( 0, 9, 9, 10, 10, 11, 12, 12, 13, 14, 15, 15, 16, 17, 18, 19, 21, 22, 23, 24, 26, 27, 29, 31, 33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, 65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 117, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, 4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902, 8372, 8870, 9397, 9956, 10548, 11175, 11840, 12544 ); var count, {Event duration counter in system timer ticks (18.2/sec)} eventnum, {Current event number} nevents: integer; {Number of events defined} event: array [1..maxevents] of record ticks: integer; {Speaker timer period} duration: integer; {Duration in system ticks} end; period: array [0..127] of integer; {Cycle length for note frequencies in spkr timer ticks (.84 microsec)} saveint: pointer; {Saved interrupt vector} function inport(x: integer): byte; inline($5a/$eb/$00/$ec); {Read port (w/short jump)} procedure timerint; interrupt; { Procedure called at each system timer interrupt. Since there may not be enough available stack space at the time of the interrupt, no procedures should be called from this routine (and runtime checks should be disabled). } begin dec(count); {Count event duration} if eventnum < nevents then if count <= 0 then {Time for next event?} begin inc(eventnum); {Get next} with event[eventnum] do begin count := duration; {Reset event timer} port[$42] := lo(ticks); {Set speaker counter value} port[$42] := hi(ticks); end; end; inline($9c/$ff/$1e/saveint); {Chain to old interrupt} end; procedure getevents; { Define some events: 12-note chromatic scale from middle C, .5 sec per note with .2 sec rest (approx)} var i, j: integer; begin i := 1; j := 60; repeat event[i].ticks := period[j]; {Note} event[i].duration := 9; i := i + 1; event[i].ticks := period[0]; {Rest} event[i].duration := 4; i := i + 1; j := j + 1; until i > 24; nevents := 24; count := 0; end; procedure init; var i: integer; begin count := 0; nevents := 0; eventnum := 0; for i := 1 to 127 do {Convert freq to counter ticks} period[i] := 1193182 div notefreq[i]; period[0] := $08; {No sound (very high frequency - quieter than turning speaker off)} getintvec($1c, saveint); {Save present timer interrupt address} setintvec($1c, @timerint); {Substitute our interrupt routine} port[$43] := $b6; {Counter 2, mode 3, LSB+MSB} port[$42] := $08; {Init timer value - no audible sound} port[$42] := $00; port[$61] := inport($61) or 3; {Enable speaker, gate counter} end; begin init; getevents; writeln('Press a key to exit'); repeat {Idle while playing - this can be anything} until keypressed; {Clean up} port[$61] := inport($61) and $fc; {Turn off speaker} setintvec($1c, saveint); {Restore old interrupt - IMPORTANT!} end.