|
Introduction
Begining with version 2.1, IBM has delivered two new disks with OS/2
containing the previously separately sold Multimedia Presentation
Manager/2 1.1 (MMPM/2). Having once visited a colleague who used a Mac
and being enamoured with the characteristic emminating from the
system speaker after he dropped some superfluous files on a toilet icon, I
installed it to try out the system sounds on my SoundBlaster-equipped
machine. Although it is quite satisfying in its own right that our beloved
operating system is now able to cope with the Mac in this particular area,
one should take the little extra time to have a closer look on the MMPM/2
for it has more to offer.
This is the first of two articles which do so. It gives an overview of
MMPM/2 and introduces programming for multimedia, using the REXX language
which is sufficient for many cases. The second article will deal with
MMPM/2 programming in C and C++. Both articles focus on the audio
capabilites of MMPM/2.
MMPM/2 Overview
Like its name suggests, the Multimedia Presentation Manager adds multimedia
capabilities to IBM's OS/2 2.1 operating system, but what does this
actually mean? For me, the term multimedia has acquired a slightly
negative connotation in recent months because it is used so extensivly by
computer hardware vendors to hawk their wares. They take a PC, put a
soundboard and a CD-ROM drive into it, drape it with two tiny speakers and
voila(!) it's perfect multimedia! The hardware is sold but the purpose is
still rather vague.
In the publication "OS/2 2.1 Technical Update" (GG24-3948-00) IBM writes:
The key benefit of multimedia is an enhanced quality of information and
communication. When audio, image, and video are combined with text and
graphics, customers can access richer forms of information, and
communication becomes more effective.
This is not a bad definition. The variety of the types of data computers
are asked to crunch has become richer over the years, starting with numbers
and text, followed by graphics, and now also images (be it photos or live
motion video) and audio data. The need for the capability to combine these
different kinds of data is also crucial.
IBM writes further:
MMPM/2 enables this increase in productivity by providing device control,
streaming, synchronization, and multimedia object I/O support.
That is a brief summary of MMPM/2. The package consists of:
- Physical Device Drivers (PDD)
- A collection of drivers for multimedia
hardware like soundcards, CD-ROM drives, video digitizers, video disc-players,
MIDI devices, etc
- Media Control Interface (MCI)
- The MCI is a programming interface which
allows the easy use of the media devices, handling them all in a common way
via command strings like open, close, play, record, stop, seek, etc.
- Synchronization & Streaming Programming Interface (SPI)
- The SPI
multimedia subsystem is responsible for the smooth execution of multimedia
applications under OS/2. It ensures that those tasks get the needed
processor time and synchronizes different streams of multimedia data.
- Multimedia I/O Services
- The MMIO subsystem extends the OS/2 file
services. It provides a standard mechanism for accessing and manipulating
multimedia data files (media elements). Supported are buffered I/O, RIFF
file I/O, memory file I/O, compound file I/O; it is also possible to add
additional I/O procedures to the MMIO subsystem
- PM Extensions
- MMPM/2 provides some new Presentation Manager window
classes tailored for the control of multimedia applications (graphical
button, circular slider, etc.)
Also several multimedia utilities are delivered with MMPM/2, like a CD
audio player, a waveaudio player and editor, software video players and
more.
This first article only deals with the MCI because it is programmable from
REXX in a very easy way and gives a nice impression of the MMPM/2
architecture. The other features will be treated in the second article.
The Media Control Interface
If you have installed MMPM/2 you'll find on your harddisk a online manual
about using the MCI from a REXX programmer's point of view
Note: this manual has many gaps! A more complete description can be found
in the MMPM/2 Programming Reference of the OS/2 Technical Library
(available in online form on the bookware CD-ROM).
To allow interaction with the multimedia hardware MMPM/2 defines a set of
media devices on top of the PDDs, which can be controled via the textual
string commands of the Media Control Interface (MCI), defined by IBM and
Microsoft in 1991.
The standard media devices are:
Device name |
Description |
CDaudio |
A CD-ROM device which supports standard audio compact disc
playback |
Digitalvideo |
A device which supports audio/video files, either
hardware-assisted or software motion video-only (e.g. it plays Intel Indeo
and IBM Ultimotion *.AVI files) |
Sequencer |
A device which supports MIDI files (*.MID) |
Videodisc |
A videodisc player |
Videotape |
A videotape player or recorder |
Waveaudio |
A device which supports digital audio files (*.WAV) |
The MMPM/2 headers also define these devices: |
Ampmix |
An amplifier/mixer device |
Animation |
DataA device for playing Autodesk Animator (*.FLC, *.FLI) and
MacroMind Director files (*.MMM) |
Audiotape |
An analog audio tape deck |
CDXA |
A CD-ROM device which supports the extended architecture (XA) audio compact disc standard (up to 8h stereo, 15h music, 30h voice quality
playback) |
DAT |
A digital audio tape deck |
Headphone |
HeadphoneS |
Microphone |
A microphone |
Monitor |
A monitor output device |
Other |
An undefined MCI device |
Overlay |
A video overlay device - video output in a window |
Scanner |
An image scanner |
Speaker |
A speaker |
These media devices are modeled closely after their real-world pendants and
their programming via the MCI command strings mirrors the actions one
usually performs on a common audio or video component. Also the controls
of the utilities delivered with the MMPM/2 have the same typical look and
feel of their real-world counterparts.
All devices are either of simple or compound type. Compound devices use
data files (in MCI-speak: can load a device element in the device
context), simple devices don't. CDaudio is a simple device, Waveaudio a
compound one.
Let's start with a simple example which should play a certain waveform
audio file. The MCI command strings one needs for this are:
open Waveaudio alias wave shareable wait
load wave c:\mmos2\sounds\boing.wav wait
play wave wait
close wave wait
First the Waveaudio device is opened in shareable mode (in MCI-speak: a
device context is created) and it gets the alias wave assigned to it. Then
using the alias to address the media device, the BOING.WAV file is loaded
into memory. A play command is issued on wave and finally the device is
closed. This also frees the memory assigned to the media device when
loading the *.WAV file. The wait keyword tells MMPM/2 to wait until the
command has finished before continuing.
Now let's play the end from "U got the look" (track 1, 3.40) to the
beginning of "if I was your girlfriend" (track 2, 0.20) from disc 2 of
Prince's "Sign o' the Times" album:
open CDaudio alias cd wait
set cd time format tmsf wait
play cd from 1:3:40 to 2:0:20 wait
close cd wait
The CDaudio device is opened and the alias cd is assigned to it. Next the
media device is told to count in tracks, minutes, seconds, and frames.
Finally, both tracks are played from the given positions and the device is
closed.
Note that the CDaudio device is directly manipulated; it is not necessary
to load a data file.
The third examples plays a software video slowed down to 25% speed. Note
that in this mode audio is switched off.
open digitalvideo alias video wait
info video product wait
load video some.avi wait
set video speed format percentage wait
play video speed 25 wait
close video wait
The digitalvideo device gets opened and video is the assigned alias. Then
the device is queried for the information product; this will create a
return string with some describing text. The file SOME.AVI gets loaded and
the format for speed settings is set to percentages. The video is then
played with 25% speed and the device is closed.
I hope the basic recipe of a MCI script was conveyed from these examples.
They illustrate that the different media devices are handled in similar
manners. Armed with a MCI manual you should be able to write your own
scripts.
To send those command strings to the interpreting Media Device Manager
(MDM) one can use either REXX, C/C++ or the String Test tool provided with
the Multimedia Toolkit.
Using the MCI from REXX
Now let's look how to realize those examples in REXX.
Example 1:
/************************************************************
boing.cmd
REXX procedure to play boing.wav via MMPM
Marc E.E. van Woerkom, 1/94
************************************************************/
rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /*register MCI REXX API*/
'mcciRxInit')
rc = call mciRxInit() /*and initialize it*/
rc = mciRxSendString('open waveaudio', /*open digital audio*/
'alias wave shareable wait',,
'RetStr', '0', '0')
boing = 'c:\mmos2\sounds\boing.wav'
rc = mciRxSendString('load wave' boing,, /*load wav*/
'RetStr', '0', '0')
rc = mciRxSendString('play wave wait',, /*play file*/
'RetStr', '0', '0')
rc = mciRxSendString('close wave wait',, /*close device "wave"*/
'RetStr', '0', '0')
call mciRxExit
exit rc /*exit with rc code*/
The first step in BOING.CMD is to register the MCI REXX application
interface using the call to RxFuncAdd() and then by initializing it via a
call to mciRxInit().
Statements of the form mciRxSendString('some_MCI_command',RetStr,'0','0')
send the MCI command string to the MDM and may receive a return string in
the RetStr variable.
Business finishes with a call to mciRxExit().
Example 2:
/************************************************************
prince.cmd
REXX procedure to play some audio CD tracks via MMPM
Marc E.E. van Woerkom, 1/94
************************************************************/
rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /*register MCI REXX API*/
'mciRxInit')
rc = call mciRxInit() /*and initialize it*/
rc = mciRxSendString('open CDaudio', /*open CDaudio*/
'alias cd wait',,
'RetStr', '0', '0')
rc = mciRxSendString('set cd time format', /*count in tracks*/
'tmsf wait ',, /*(tt:mm:ss:ff)*/
'RetStr', '0', '0')
say rc
rc = mciRxSendString('play cd from 1:3:40 to 2:0:20 wait',, /*play file*/
'RetStr', '0', '0')
if rc <> 0 then do
rc2 = mciRxGetErrorString(rc, 'ErrStVar')
say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
exit rc2
end
rc = mciRxSendString('close cd wait',, /*close device "cd"*/
'RetStr', '0', '0')
say rc
call mciRxExit
exit rc /*exit with rc code*/
This example also uses the function mciRxGetErrorString() to receive the
error message belonging to the return value RC.
Example 3:
/************************************************************
playvid.cmd
REXX procedure to play a software video at 25% speed
usage: pmrexx playvid name.avi
Marc E.E. van Woerkom, 1/94
************************************************************/
parse arg avi /*fetch first argument*/
if avi = '' then do
say 'usage: pmrexx playvid name.avi'
exit 0
end
say '(Playing a Software Video at 25% speed ...'
rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /*register MCI REXX API*/
'mciRxInit')
rc = call mciRxInit() /*and initialize it*/
rc = mciRxSendString('open digitalvideo alias', /*open player*/
'video wait',,
'RetStr', '0', '0')
rc = mciRxSendString('info video', /*ask for product info */
'product wait',,
'videoinfo', '0', '0')
say ' Digital Video Device is' videoinfo
rc2 = mciRxSendString('load video' avi,, /*play *.avi */
'RetStr', '0', '0')
rc = mciRxSendString('set video speed', /*set speed in % */
'format percentage wait',,
'RetStr', '0', '0')
rc = mciRxSendString('play video speed 25 wait',, /* play file */
'RetStr', '0', '0')
rc = mciRxSendString('close video wait',, /*close video device*/
'RetStr', '0', '0')
if rc2 <> 0 then do
rc = mciRxGetErrorString(rc2, 'ErrStVar')
say ' ERROR! rc =' rc2 ', ErrStVar =' ErrStVar
say ' ... failed!)'
exit rc
end
else say ' ... done.)'
call mciRxExit
exit rc /*exit with rc code*/
This one starts with a parsing of the command line arguments. Later the
example queries the digitalvideo device for a product description string
which is delivered into the videoinfo variable.
Note that the digitalvideo device needs the Presentation Manager, so if
this REXX procedure is to be used from the command line, one has to call it
through PMREXX.
Example 4:
/************************************************************
cdlock.cmd
REXX procedure to lock the CD device
usage: cdlock
Marc E.E. van Woerkom, 11/93
************************************************************/
say '(Locking the CD drive''s door ...'
rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /*register MCI REXX*/
'mciRxInit')
rc = call mciRxInit() /*and initialize it*/
rc = mciRxSendString('open CDaudio alias cd', /*open CDaudio*/
'shareable wait',,
'RetStr', '0', '0')
rc2 = mciRxSendString('set cd door locked wait',, /*lock CD door*/
'RetStr', '0', '0')
rc = mciRxSendString('close cd wait',, /*close device "cd"*/
'RetStr', '0', '0')
if rc2 <> 0 then
say ' ... failed!)'
else
say ' ... done.)'
call mciRxExit
exit rc2 /*exit with rc2 code*/
Did you notice already the menu item lock disk in the context menu of the
CD-ROM drive icon? As does that menu item, this procedure locks the door
of the CD-ROM drive so that it can't be opened with a push on the external
open/close button.
Example 5:
/************************************************************
cdunlock.cmd
REXX procedure to unlock the CD device
usage: cdunlock
Marc E.E. van Woerkom, 11/93
************************************************************/
say '(Unlocking the CD drive''s door ...'
rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /*register MCI API*/
'mciRxInit')
rc = call mciRxInit() /*and initialize it*/
rc = mciRxSendString('open CDaudio alias cd', /*open CDaudio*/
'shareable wait',,
'RetStr', '0', '0')
rc2 = mciRxSendString('set cd door unlocked wait',, /*unlock CD door*/
'RetStr', '0', '0')
rc = mciRxSendString('close cd wait',, /*close device "cd"*/
'RetStr', '0', '0')
if rc2 <> 0 then
say ' ... failed!)'
else
say ' ... done.)'
call mciRxExit
exit rc2 /* exit with rc2 code*/
This procedure unlocks the CD-ROM drive, reenabling the external open/close
button.
Example 6:
/************************************************************
cdopen.cmd
REXX procedure to open the CD door
usage: cdopen
Marc E.E. van Woerkom, 11/93
************************************************************/
say '(Opening the CD drive''s door ...'
rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /*register MCI REXX API*/
'mciRxInit')
rc = call mciRxInit() /*and initialize it*/
rc = mciRxSendString('open CDaudio alias cd', /*open CDaudio*/
'shareable wait',,
'RetStr', '0', '0')
rc2 = mciRxSendString('set cd door open wait',, /*open CD door*/
'RetStr', '0', '0')
rc = mciRxSendString('close cd wait',, /*close device "cd"*/
'RetStr', '0', '0')
if rc2 <> 0 then
say ' ... failed!)'
else
say ' ... done.)'
call mciRxExit
exit rc2 /*exit with rc2 code*/
This procedure opens the door of your CD-ROM drive and ejects the tray. I
admit, it is a bit drole (somehow it reminds me of little R2-D2 from Star
Wars) but I'd be grateful if someone points out a serious application of
this to me.
Example 7:
/************************************************************
playall.cmd
REXX procedure to play all *.wav *.mid
usage: playall directory [wav] [midi] [rec]
Marc E.E. van Woerkom, 1/94
************************************************************/
parse arg dir arg1 arg2 arg3 /*copy argv[1] and remove*/
say
if dir = '' then do
say 'PlayAll seeks and plays all soundfiles'
say
say 'usage: playall directory [wav] [midi] [rec]'
exit 0
end
if dir = '.' then dir = directory() /*current dir*/
dowav = 0
domidi = 0
dorec = 0
parse upper arg dummy arg1 arg2 arg3 /*copy argv[1..3]*/
if arg1 = 'WAV' then dowav = 1
if arg1 = 'MIDI' then domidi = 1
if arg2 = 'MIDI' then domidi = 1
if arg1 = 'REC' then dorec = 1
if arg2 = 'REC' then dorec = 1
if arg3 = 'REC' then dorec = 1
if (\dowav) & (\domidi) then do
dowav = 1
domidi = 1
end
files = '' /*message*/
if dowav then files = ' *.wav'
if domidi then files = files' *.mid'
opt = 'O' /*dir tree options*/
if dorec then do /*F: files only*/
opt = opt'S' /*O: only qualified name*/
recstr = 'and beneath ' /*S: recurse*/
end
else recstr = ''
say '(Playing all'files 'files from' dir recstr'...'
say
rc = call RxFuncAdd('SysLoadFuncs', 'RexxUtil',, /*register REXX Utils*/
'SysLoadFuncs')
rc = call SysLoadFuncs() /*and initialize them*/
rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /*register MCI REXX API*/
'mciRxInit')
rc = call mciRxInit() /*and initialize it*/
if dowav then do
rc = mciRxSendString('open waveaudio', /*open digital audio*/
'alias wave shareable wait',,
'RetStr', '0', '0')
if rc <> 0 then do
rc2 = mciRxGetErrorString(rc, 'ErrStVar')
say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
exit rc2
end
rc = mciRxSendString('info wave', /*ask for product info*/
'product wait',,
'waveinfo', '0', '0')
rc = mciRxSendString('close wave wait',, /*close device "wave"*/
'RetStr', '0', '0')
say ' Waveaudio device is' waveinfo
end
if domidi then do
rc = mciRxSendString('open sequencer', /*open MIDI device*/
'alias midi2 shareable wait',,
'RetStr', '0', '0')
if rc <> 0 then do
rc2 = mciRxGetErrorString(rc, 'ErrStVar')
say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
exit rc2
end
rc = mciRxSendString('info midi2', /*ask for product info*/
'product wait',,
'midi2info', '0', '0')
rc = mciRxSendString('close midi2 wait',, /*close device "midi"*/
'RetStr', '0', '0')
say ' MIDI device is' midi2info
end
if Right(dir, 1) = '\' then
mask = dir'*.*'
else
mask = dir'\*.*'
rc = call SysFileTree(mask, 'file', opt) /*read dir tree*/
if file.0 = 0 then do
say ' No files found!'
rc = 0
end
do i=1 to file.0
ext = Translate(Right(file.i, 4))
if dowav & (ext = '.WAV') then do
say
say ' File' file.i':'
rc = mciRxSendString('open waveaudio', /*open digital audio*/
'alias wave shareable wait',,
'RetStr', '0', '0')
rc = mciRxSendString('set wave time', /*set time format to ms*/
'format ms wait',,
'RetStr', '0', '0')
rc = mciRxSendString('load wave' file.i 'wait',, /*play *.wav*/
'RetStr', '0', '0')
rc = mciRxSendString('status wave length wait',, /*query length*/
'RetStr', '0', '0')
if rc = 0 then do
time = RetStr / 1000.0
say ' Playing time is' time 's'
end
rc = mciRxSendString('play wave wait',, /*play file*/
'RetStr', '0', '0')
rc = mciRxSendString('close wave wait',, /*close device "wave"*/
'RetStr', '0', '0')
end
if domidi & (ext = '.MID') then do
say
say ' File' file.i':'
rc = mciRxSendString('open sequencer', /*open MIDI device*/
'alias midi2 shareable wait',,
'RetStr', '0', '0')
rc = mciRxSendString('set midi2 time', /*set time format to ms*/
'format ms wait',,
'RetStr', '0', '0')
rc = mciRxSendString('load midi2' file.i 'wait',, /*play *.mid*/
'RetStr', '0', '0')
rc = mciRxSendString('status midi2 length wait',, /*query length*/
'RetStr', '0', '0')
if rc = 0 then do
time = RetStr / 1000.0
say ' Playing time is' time 's'
end
rc = mciRxSendString('play midi2 wait',, /*play file*/
'RetStr', '0', '0')
rc = mciRxSendString('close midi2 wait',, /*close device "midi"*/
'RetStr', '0', '0')
end
end
say
if rc = 0 then
say '... done.)'
else
say '... playing aborted!)'
call mciRxExit
exit rc /*exit with rc code*/
I wrote this procedure to try out a whole directory of audio files at once.
To play all *.WAV files from the current directory and all directories
under it, type playall . wav rec on the command line.
Literature on MMPM/2
Dick Conklin (Ed.):
OS/2 2.x Notebook - The Best of OS/2 Developer Magazine.
Van Nostrand Reinhold, New York, 1993.
IBM Doc. S41G-2919:
MMPM/2 Programming Guide.
"Describes application and subsystem programming interfaces to help you
select and implement functions for OS/2 multimedia applications"
IBM Doc. S41G-2920:
MMPM/2 Programming Reference.
"Provides detailed information on multimedia functions, messages, and data
structures to enable you to write code for multimedia applications"
IBM Doc. S41G-2921:
MMPM/2 Sample Programs Workbook.
"Gives code examples from MMPM/2 sample application programms and
templates for subsystem components"
IBM Doc. S41G-2922:
CUA Guide to Multimedia User Interface Design.
"Describes design concepts to consider when designing a CUA multimedia
interface"
IBM Doc. S41G-2923:
OS/2 Multimedia Advantages.
"Describes advantages offered by OS/2 and MMPM/2 multimedia platform"
IBM Doc. S41G-3321:
Complete MMPM/2 Technical Library.
John Musser:
A Multimedia Class Library for Windows.
Dr. Dobb's Journal, 7:84-90, 1993.
|
|