Apple Enterprise - NeXTanswers Support Archive
Enterprise
[Index]
[Download]


Search NeXTanswers for:

NeXTSTEP Programming Kits Q&A



Creation Date: July 12, 1998
Keywords:
SoundKit, NetInfoKit, DBKit, IndexingKit, MusicKit

Disclaimer

This document pertains to the NeXTSTEP operating system, which is no longer a supported product of Apple Computer. This information is provided only as a convenience to our customers who have not yet upgraded their systems, and may not apply to OPENSTEP, WebObjects, or any other product of Apple Enterprise Software. Some questions in this Q&A document may not apply to version 3.3 or to any other specific version of NeXTSTEP. Some or all of the programming interfaces referred to in this document may no longer be supported in any current product.

SoundKit Questions


Q: Why doesn't SoundView's setReductionFactor: work in Release 3?

A: There is a known bug in the SoundView's setSound: method which resets the reductionFactor even if you are explicitly setting it yourself. If you wish to use your own reductionFactor, you need to call setReductionFactor: after each call to setSound:.

The SoundEditor example released in 3.0 uses setReductionFactor:, but it doesn't work correctly because of this bug.

A point of confusion is that the reduction methods--setReduction and reduction--have been removed from the API for 3.0, but the reductionFactor methods--setReductionFactor and setReduction--are still part of the API.

Valid for 3.0, 3.1

Q: How can I determine a Sound's duration?

A: Send the Sound the sampleCount and samplingRate methods (if necessary) and divide the former number by the latter.

Q: How can I get Sounds to repeat one after the other, or the same one multiple times?

A: There are several approaches; however, none are guaranteed to work in all possible situations.

1) You can compute the duration of each sound just before you play it (following the approach in the answer to the first question). Set up a timed entry (see documentation on DPSAddTimedEntry) that occurs at a time equal to the sound's duration plus any desired delay between playings. When you get this timed entry, you tell the next Sound to play, and so forth.

2) If you don't require an Application event loop, you can reliably chain sounds together using the Sound library C functions. See the example file /NextDeveloper/Examples/Sound/chaintest.c, and the README file in the same directory. You are advised to use the SNDNotificationFun approach, as in this example, and to avoid the SNDWait() function.

3) If you require an Application with an event loop (the normal case), you might have success using the SoundKit delegation method didPlay:. Create an object that is the Sound's delegate, or use an existing object. Provide it with these methods:

int sndCtr;

- init {
[mySound setDelegate:self]; /* mySound is the Sound to be looped */
sndCtr = 1;
}

- playIt {
[mySound play];
}

- didPlay:sender {
if (sndCtr++ < NUMTIMES)
[self playIt]; /* play it NUMTIMES in a row. Alternately, use some other terminating condition, or trigger other Sounds here */
}

This approach sometimes causes problems in the playback, however, particularly if the user interrupts the playing with an event such as a mouse-down. You'll have to try it for your specific case and see whether it works.

4) If none of these approaches is suitable, the workaround is to create a new Sound composed of multiple copies of the desired sound(s), using the SoundKit's copy/paste functionality. You can do this splicing programmatically with the insertSamples:at: method, or manually by using the SoundEditor example.

5) As a final option, you can combine the chaintest strategy with the SoundKit example above. Override Sound's "play" method and directly enqueue the soundstruct with SNDStartPlaying, giving it your own special SNDNotificationFun to do the chaining as in chaintest. The drawback of this approach is that you cannot safely send the delegate a soundDidPlay: message when the repeat count runs out.

Valid for 1.0, 2.0, 3.0

Q: I am porting my sound driver program from Release 2 to 3. But the kernel header file snd_msgs.h is no longer available in Release 3. What can I do to get a handle on the error codes returned by the SNDDRIVER functions?

A: You can include the following code in your program to provide for error handling:

#import <sound/sounderror.h> /* for SNDSoundError() */

#ifndef SNDDRIVER_NO_ERROR
#define SNDDRIVER_NO_ERROR 100 // non-error ack.
#define SNDDRIVER_BAD_PORT 101 // message sent to wrong port
#define SNDDRIVER_BAD_MSG 102 // unknown message id
#define SNDDRIVER_BAD_PARM 103 // bad parameter list in message
#define SNDDRIVER_NO_MEMORY 104 // can't allocate memory (record)
#define SNDDRIVER_PORT_BUSY 105 // access req'd to existing excl access port
#define SNDDRIVER_NOT_OWNER 106 // must be owner to do this
#define SNDDRIVER_BAD_CHAN 107 // dsp channel hasn't been inited
#define SNDDRIVER_SEARCH 108 // couldn't find requested resource
#define SNDDRIVER_NODATA 109 // can't send data commands to dsp in this mode
#define SNDDRIVER_NOPAGER 110 // can't allocate from external pager (record).
#define SNDDRIVER_NOTALIGNED 111 // bad data alignment.
#define SNDDRIVER_BAD_HOST_PRIV 112 // bad host privilege port passed.
#define SNDDRIVER_BAD_PROTO 113 // can't do requested operation in cur protocol
#define SNDDRIVER_MAX_ERROR 113
#endif

static char *snddriver_error_list[] = {
"sound success",
"sound message sent to wrong port",
"unknown sound message id",
"bad parameter list in sound message",
"can't allocate memory for recording",
"sound service in use",
"sound service requires ownership",
"DSP channel not initialized",
"can't find requested sound resource",
"bad DSP mode for sending data commands",
"external pager support not implemented",
"sound data not properly aligned",
"bad host provilege port passed",
"can't do requested operation in extant DSP protocol"
};

/*
* Error code to string conversion
*/
extern char *mach_error_string();
char *snddriver_error_string(int error)
{
if (error <= 0)
return mach_error_string(error);
else if (error >= SNDDRIVER_NO_ERROR && error <= SNDDRIVER_MAX_ERROR)
return snddriver_error_list[error-SNDDRIVER_NO_ERROR];
else
return SNDSoundError(error);
}

void snddriver_error(char *msg,int error)
{
char *errtype;
if (error <= 0)
errtype = "mach";
else if (error >= SNDDRIVER_NO_ERROR && error <= SNDDRIVER_MAX_ERROR)
errtype = "snddriver";
else
errtype = "sound";
fprintf(stderr,"%s\n\t%s error:%s\n",msg,errtype,snddriver_error_string(error));
}


NetInfo Kit Questions


Q: I've read the release notes about the NetInfoKit (NIKit). Is there additional information about these objects and how to use them?

A: The only documentation that is currently available are the on-line Release Notes on 2.0 and whatever you can glean from the .h files. As you saw from those notes, it provides you with a rough overview of the objects available in the NIKit, but doesn't provide you with any reference material. Through this document, we hope to shed some light on the NIKit objects, how to use them, and for what purpose.

Feedback for these objects would be greatly appreciated by the folks in engineering. Please send your feedback to bug_next@next.com.

NIDomain object
The first object, the NIDomain object, was created so that the other objects could have something through which to communicate to the NIKit database. The panel objects (like NIOpenPanel) use NIDomain to set and retrieve data from the NetInfo database. This object is the most likely one to change in future releases. You probably won't use NIDomain directly in your application, since the panel objects provide a higher level interface to it.

NIDomainPanel Object
The NIDomainPanel object is essentially a browser. It browses through the domain hierarchy, and displays it (much like the file system browser). To use the NIDomainPanel you should follow these steps:

- new
returns an id for the new instance of the NIDomainPanel.
- runModal
this runs a modal loop which exits when the user clicks ok or cancel
- exitFlags
returns whether the user clicked ok or cancel
- domain
returns the name of the domain that the user has selected.

The other methods are primarily there in case you wish to override them and provide some special functionality. Most useful are cancel, ok and domain.

NIOpenPanel Object
The next object, NIOpenPanel is a sub class of NIDomainPanel. It is used to display and select a particular directory from a list of its peers inside of any NetInfo domain in the hierarchy. It's analogous to the OpenPanel for filesystems. NIOpenPanel consists of the domain browser (which it inherits from the NIDomainPanel), buttons and a scrolling list of directories. You create an NIOpenPanel the same way in which you create a NIDomainPanel: using new and runModal. The other really interesting methods in NIOpenPanel are the directory and setDirectoryPath methods which you can use to programmatically browse to the directory path that you want from inside of the domains, or ask the NIOpenPanel where it currently is browsed to.

NISavePanel Object
NISavePanel is a subclass of NIOpenPanel, and is used to assist in saving changes to an open directory in a particular domain. NISavePanel is very much like NIOpenPanel, but with some additional functionality. It provides three different runModal routines. runModal is the one which you will use most frequently. It will start such that the browser is displaying your current domain. runModalWithString runs a modal loop, and starts up such that the browser is displaying the domain which is passed in through "initialValue". runModalwithUneditableString is the same except for the fact that the directory is uneditable. It is used this way inside the NetManager and UserManager where the name of the directory is a fundamental property of the machine or user being edited and cannot be changed on the fly while performing a Save To.


NILoginPanel Object
And finally the NILoginPanel. This is probably the object for which you will have the most use. The login panel is used to verify (authenticate) a user's identification. You can either rely on the user id and password that are stored in the NI database, or you can use your own. There are three runModal methods in this object as well:

- (BOOL)runModal:sender inDomain:(void *) domainID;
This runs the login dialog modally with the default user "root".

- (BOOL)runModal:sender withUser:(char *)userName inDomain:(void *)domainID
withString:(const char *)whatWarning allowChange:(BOOL)disableUser;
This runs the login dialog modally with the user as specified. You can change the message
displayed on the panel by using the withString argument. Also, this method allows the user to
edit the default user name.

- (BOOL)runModalwithValidation:sender withUser:(const char *)userName
inDomain:(void *) domainID withString:(const char *)whatWarning
allowChange:(BOOL)enableUser;
This runs the login dialog modally as the previous method. However, this one requires validation.
With this modal method you can provide your own password validation scheme. You use this
in conjunction with the NILoginPanelDelegate's authenticateUser method.

At this point there is no example code from which you can learn to use these objects wisely (or at all). For examples about how these NIKit objects look and for an idea of how they can be put to good use, you can look at the system administration tools in /NextAdmin. Most of them use the NIKit. Future changes in the NIKit will be made to better support these applications.

Valid for 2.0, 3.0


DBKit Questions


Q: I put together a Database Kit program using InterfaceBuilder and it works fine in InterfaceBuilder's test mode. When I compile this program and try to run it independently, an attention panel opens and indicates: "Cannot load Adaptor: Oracle Adaptor." Why?

Q: I build a Database Kit application and add libdbkit_s.a to the Libraries directory in ProjectBuilder. Why do I still get the following error message in the console:

Error loading /NextLibrary/Adaptors/SybaseAdaptor.adaptor/SybaseAdaptor
rld(): Undefined symbols:

In GDB, the undefined symbols are reported as _sys_nerr, _sys_errlist, and _send.

A:
In order for a Database Kit application to work, adaptor code must be made available to it. There are two ways to achieve this:

1) Hard-link an adaptor into your application. To do this, simply drag the adaptor you want onto the Libraries icon in ProjectBuilder's Files window. The adaptors provided by NeXT are located in /NextLibrary/Adaptors.

2) Dynamically load the adaptor code. To do this, you need to create a file in your project directory called Makefile.preamble, which contains the following line:

OTHER_LDFLAGS = -u libdbkit_s -u libNeXT_s -u libsys_s

Q:
What are the pros and cons of dynamically loading adaptor code?

A:
Pros
You can write generic tools, without knowing which database they will be run against. Your application gets the latest and greatest version of adaptors, in case NeXT revises them.

Cons
Dynamically loading an adaptor slows down program startup (often by several seconds)

Q: How can I get hold of the DBBinder used by my application's DBDatabase object?

A: You can't. If you want to use DBBinders in your application, you have to create them yourself. See /NextDeveloper/Examples/DatabaseKit/Binder for some usage of a DBBinder.

Q: How can I view the SQL queries generated in my program?

A: You can trace the actual SQL queries generated by the Database Kit by having an object becoming the DBDatabase object's delegate. The delegate object must implement the db:willEvaluateString:usingBinder: method. For more details, see Controller.m in the /NextDeveloper/Examples/DatabaseKit/AddressBook example.

Q: What are the pros and cons of using one DBModule vs. two to implement insert/save in the case of a one-to-many relationship?

A: In case of 2 DBModules (one for the master table and one for the detail table), you don't have to find the detail fetchgroup programmatically. You can insert a new record into the detail module by simply connecting a button to the detail module insertNewRecord: method. When saving, you need to save the master module first, then save the detail module. See the /NextDeveloper/Examples/DatabaseKit/OracleDemo for an example of two modules.

In case of a single DBModule (representing the master table), you need to find the detail fetchgroup programmatically in order to insert a new record. However, you need to do only one save operation on the module, and it saves all changes from the root fetchgroup as well as the detail fetchgroup(s). The module save operation groups both fetchgroup saves as a single transaction.

Q: How can I synchronize a master DBModule with a detail DBModule ?

A: Just drag and drop the one-to-many relationship in the master DBModule onto the icon of the detail DBModule in the nib file. See /NextDeveloper/Examples/DatabaseKit/OracleDemo for more details on how to synchronize two modules.

Q:
How do I connect a property of my table to a PopUpList object? I've tried connecting an attribute of my table to the PopUpList but nothing happens. The PopUpList still shows item 1, item 2, etc.

A: PopUpList connections can be made only to a field "x.y.z" where x is an entity, y is a one-to-one relationship, and z is a leaf-node field that is either a number or a string. For example, if you connect the attribute "name" from your Employee table to the PopUp, it only shows item1, item 2, etc. However, if you connect "Employee.Department.name", it shows all the names in the department.

Q: Which Application Kit objects are supported by the Database Kit?

A: The Database Kit contains various subclasses of DBAssociation designed to connect DBFetchGroups to certain Application Kit objects. Those Application Kit objects are:


TextField
Slider
Matrix of Cells or FormCells
NXBrowser
Text Object in a ScrollView
Button connected to a PopUp menu

Note:
Radio buttons and switches are not supported. See the RadioAssociation MiniExample for a workaround (radio buttons only).


Q: The model that I built with DBModeler shows only the views and tables that I own. How can I access all the views and tables in an Oracle database?

A: By default, DBModeler loads only those tables and views owned by the current user. However, you can extend the list of users for whom tables and views should be loaded. To do so, add the following to the defaults database--for example, to show the tables and views owned by the users David, Joe, Tony:

localhost> dwrite OracleAdaptor OracleTableOwners "'DAVID','JOE','TONY'"

The user names have to be quoted and the current user name has to be included as well. In order to load system tables, you can simply add the users 'SYSTEM' and 'SYS'. You can modify or change your list by using dremove, then dwrite. See the UNIX man pages for more information on dread, dremove, and dwrite. Note that this answer does not cover issues about access rights to system tables. Please refer to the Oracle manuals for further information.

Q: What should I do on my NEXTSTEP system in order to use my Oracle server with the Database Kit?

A: You need to do the following:
On the server side:

1) Check if oraserv is running.

2) Check if you have a file oratab under /etc. The oratab file has entries of the following format:

$ORACLE_SID:$ORACLE_HOME:<N|Y>
The entries in that file must match the entries you give to DBModeler. See item#4d. Specify "Y" so that the server can be started with dbstart at system boot up time.

On the client side:

3) Look at your /etc/services file:
a) Check with the Oracle DBA to verify that the database is using socket 1525 (this is the default). Enter the following into /etc/services:

orasrv 1525/tcp oracle

b) Niload the Oracle services. Be sure to check with the Oracle DBA to verify that the database is using socket 1525 (this is the default). If you want your entire network of NeXT machines to see the server, niload the entry in the root NetInfo domain on the master NetInfo server.

localhost> su
localhost# niload services / </etc/services

4) To access an Oracle server over TCP/IP.

a) Launch /NextDeveloper/Apps/DBModeler.app

b) Select the OracleAdaptor from the Preferences menu item.

c) Choose "New Model."

d) For the Oracle login panel, enter the appropriate information:

server id: <whatever ORACLE_SID is set to>
host: <server machine name>
login:
<user login>
password:
<user password>

e) Save the model in ~/Library/Databases, /LocalLibrary/Databases, /NextLibrary/Databases or /usr/local/Databases. These are the default search paths that InterfaceBuilder uses to load models into the DatabaseKit palette.

f) From InterfaceBuilder, drag a DBModule palette (the lower left icon in the DBKit palette view) into the IB Objects suitcase. This operation creates an instance of DBModule. You can look at your model via the DBModule inspector and start creating your DBKit application.

Q: Why can I no longer load the Sybase/Oracle adaptor after I run make install on my DBKit application?

A: The problem is that make install does a complete stripping of the executable to save space by default. In order to force Makefile to recognize that DBKit adaptors need to be dynamically loaded, you must add the following line to your Makefile.postamble:

APP_STRIP_OPTS = $(DYLD_APP_STRIP_OPTS)

See also /NextDeveloper/Makefiles/apps/app.make for a full description of the rules.

Q: When I try to statically link an adaptor into my MAB [Multiple Architecture Binary] Database Kit application using NextStep Release 3.1 or 3.2, I get the following error at compilation time:

ld: for architecture i386
ld: warning /NextLibrary/Adaptors/SybaseAdaptor.adaptor/SybaseAdaptor cputype (6, architecture m68k) does not match cputype (7) for specified -arch flag: i386 (file not loaded)

ld: for architecture m68k
ld: warning /NextLibrary/Adaptors/SybaseAdaptor.adaptor/SybaseAdaptor cputype (7, architecture i386) does not match cputype (6) for specified -arch flag: m68k (file not loaded)

How can I overcome these errors?

A: The adaptors provided with Release 3.1 or 3.2 are for one single architecture type only. For example, to build a MAB Sybase Adaptor file, you need to do the following:

* Get the 2 versions of the file SybaseAdaptor from the directory /NextLibrary/Adaptors/SybaseAdaptor.adaptor . For convenience, we rename them respectively SybaseAdaptor_m68k and SybaseAdaptor_i386 in this example.
* Use the Unix command lipo(1) to create a MAB adaptor file from these 2 input files. For example:

myhost> lipo SybaseAdaptor_m68k SybaseAdaptor_i386 -create -output ~/Library/Adaptors/SybaseAdaptor_MAB

Now, you can hard link the adaptor into your MAB application. See also adaptor_linking.rtf.

Q: When I use the DBBinder evaluateString method to issue SELECT statements with an Oracle adaptor and retrieve data from my tables, the LONG column values are always returned as NULL. What is going wrong?

A: This is a known problem with the Oracle Adaptor in Releases 3.1 and 3.2. If you use InterfaceBuilder and the DBModule fetch mechanism to retrieve data from LONG columns, it works properly. However, if you must use a binder object, you need to set up your properly list before calling the binder evaluateString. For example, if you have a table A with two columns attr1 and attr2, where attr1 is defined as integer, and attr2 is defined as LONG, you need to create a property list that includes these two attributes, and initialize the binder accordingly. See the code snippet below as an example:


/* Initialize the binder and set it up */
DBBinder *binder = [[DBBinder alloc] init];
List *resultList = [[List alloc] init];
List *props = [[List alloc]init];

[binder setDatabase:myDatabase];
[binder setContainer:(id)resultList];

/* This is the workaround to the LONG datatype problem.
You must create a property list that contains all the
columns you want to select in your query before doing
the evaluateString. */
myTable = [myDatabase entityNamed:"A"];
[props addObject:[myTable propertyNamed:"attr1"]];
[props addObject:[fooTable propertyNamed:"attr2"]];
[binder setProperties:props];
result = [binder evaluateString:"select * from A"];

/* Do the rest of the work, such as fetching data,
freeing the binder and other resources after they are used, etc...*/

Q: Whenever I try to run make profile against my DBKit application, I get the following error:

cc -g -pg -O -Wall -DPROFILE -I./sym -arch i386 -ObjC -u libdbkit_s -u libNeXT_s -u libsys_s -sectcreate __ICON __header OrderByTest.iconheader -segprot __ICON r r -sectcreate __ICON app /usr/lib/NextStep/Workspace.app/application.tiff -o OrderByTest.profile/OrderByTest i386_profile_obj/OrderBy.o i386_profile_obj/OrderByTest_main.o /NextLibrary/Adaptors/OracleAdaptor.adaptor/OracleAdaptor -ldbkit_s -lMedia_s -lNeXT_s
ld: multiple definitions of symbol _exit
/lib/gcrt0.o definition of _exit in section (__TEXT,__text)
/lib/libsys_s.a(exit.o) definition of absolute _exit (value 0x50023f6)
*** Exit 1
Stop.
*** Exit 1
Stop.


What's happening?

A: Since profiling with bundles is not possible, you have to hard-link the DBKit adaptor you are using instead of dynamically loading it. For example, if you are using the Sybase Adaptor, you can hard-link it by adding the following line to your Makefile.preamble:

OTHER_OFILES = /NextLibrary/Adaptors/SybaseAdaptor.adaptor/SybaseAdaptor

Since you are no longer using dynamic loading, you also need to remove all of the -u commands in the LDFLAGS or OTHER_LDFLAGS option from your Makefile.preamble. For example, you need to delete this line from your Makefile.preamble:

OTHER_LDFLAGS = -u libdbkit_s -u libNeXT_s -u libsys_s

Now, you should be able to link and profile your application as desired. Note that if you want to profile DBKit, you need to link with the profiled version of DBKit, libdbkit_p.a, instead of libdbkit_s.a.

Valid for 3.0, 3.1

Q: I have put together a DBKit program with InterfaceBuilder and it uses DBModule to make connections between UI Objects and Record List. The DBModule is the textDelegate for the instance TextFields. I type some characters into a TextField, and, without pressing Return or Tab, I clicked the Save button (or chose the Save menu command). I subsequently discovered that the data in the very last TextField was not saved. Why?

A: The problem is that textWillEnd:endChar: is not invoked when you click a button or choose a menu command without pressing Return, Tab or back Tab first. You need the following code to get the lastly-edited value in the action method of the Button or Menu item.

- save: sender
{
id association = [dbModule editingAssociation];
id fetchGroup = [association fetchGroup];
[fetchGroup takeValueFromAssociation: association];
...
...
}

Q: In DBModeler, how do I describe a multiple-field unique key for an entity? If a single property serves as a unique key for a record--such as "employee id" might serve for an employees entity--I can select the Unique Key box in the DBModeler Inspector. But how do I create a multiple-field key for a "sales" entity in which to identify a sale uniquely, I have to look at seller, buyer, and date?

A: You may check the "Unique Key" box in as many properties of an entity as you like. This is interpreted as a multiple-field key.

Q: I am having a problem when I have only one record in a tableview. I cannot delete it using the DBModule method deleteRecord:. When I print out the generated SQL, I only see the following:
BEGIN TRANSACTION
COMMIT TRANSACTION
What's happening?

A: This is a bug introduced in Release 3.2. To work around this problem, you should send a saveModifications message to the DBRecordList which matches your DBModule to save the deletion to the database. For example, you can use the following code snippet as a wrapper around your delete method:

delete:sender
{
[dbModule deleteRecord:sender];
#ifdef BUG_WORKAROUND
[[[dbModule rootFetchGroup] recordList] saveModifications];
#endif
return self;
}

Note that this bug will be fixed in a future release, thus you may need to put a #ifdef around the workaround. Also, the same code snippet can run on Release 3.1 without any effect.

Valid for 3.2 only


Q: What are the known problems with DBKit?

A: These are the known problems:

Sorting Records

Sorting cannot be set from InterfaceBuilder. To specify a sort order, you use the DBRecordList method addRetrieveOrder:(DBRetrieveOrder)anOrder for:(id<DBProperties>)aProperty;

A problem is that when you reset the DBRecordList using the setProperties: method, it also resets the sort order. Both DBFetchGroup and DBModule send a setProperties: message before fetching. So, if you try to interpose in the fetch with a custom object, set up the sort order, then fetch, the sort order won't be set anymore. The way to get around this is to be the DBFetchGroup's delegate and respond to the fetchGroupWillFetch: message, and to set up the sort order on the sender's DBRecordList.

See the /NextDeveloper/Examples/DatabaseKit/TableView which changes the sort order every time you move the columns of the DBTableView.


Sybase Adaptor
* The adaptor doesn't infer relationships from the database.
* The adaptor doesn't read in system tables.

Oracle Adaptor
* The adaptor doesn't handle sequence numbers.
* The adaptor doesn't read relationships from the database.
* The adaptor doesn't handle catalog of databases.

DBModeler

* DBModeler doesn't import relationship information from the database into the dbmodels.

Keys
* You cannot update the primary key of your record. The only way is to delete the old record, do a save operation, and insert the new record with the modified key.

Qualifiers
* In general, all expressions in the qualifier need to be attributes or relationships coming from the qualifier's root entity. Only one-to-one relationship attributes can be built into the qualifier. There is a known bug however with fetching with a qualifier built from one-to-one relationship attributes. As a workaround, you need to invoke the DBFetchGroup method addExpression: or create an expression implicitly using a connection to the File Owner. This bug has been fixed in Release 3.2.

* To qualify records fetched from a detail DBFetchGroup in case of a one-to-many relationship, you must use custom code. See the /NextDeveloper/Examples/DatabaseKit/AssociationSybase or AssociationOracle for more details.

* There is a known problem with parenthesizing qualifiers. As a workaround, follow the steps below:

Given the following qualifier:

[qual1 initForEntity:e FromDescription:"name = 'SMITH' OR name = 'JONES'"];
[qual1 addDescription:" AND age < 10 OR age > 20"];

This generates an SQL with no parenthesized grouping:

"name = 'SMITH' OR name = 'JONES' age < 10 OR age > 20"

The workaround is as follows:

[qual1 initForEntity:e FromDescription:"name = 'SMITH' OR name = 'JONES'"];
[qual2 initForEntity:e FromDescription:"(%@) AND (age < 10 OR age > 20)", qual1];

or more generally:

[qual1 initForEntity:e FromDescription:"name = 'SMITH' OR name = 'JONES'"];
[qual2 initForEntity:e FromDescription:"age < 10 OR age > 20"];
[qual3 initForEntity:e FromDescription:"(%@) AND (%@)", qual1, qual2];

These qualifiers then generate:

"(name = 'SMITH' OR name = 'JONES') AND (age < 10 OR age > 20)"


Update
* The Database Kit doesn't support modifying columns that are made of derived data (i.e. calculated expressions and the results of a join). For example, the following scenario does not work:

You have a module through which you fetch data for a few properties in that entity, including one property through the join. The fetch works fine, but when you update using saveChanges:, you get an attention panel titled "Database" with the message "Error occurred during transaction". The workaround is to add another module and fetch the data previously accessed through the join directly. Then, you'll have no problems resaving.


IndexingKit Questions


Q: I need to customize the two methods source:aSource willWriteRecord:(unsigned)record and source:aSource didReadRecord:(unsigned)record from the IXRecordTranscription protocol to do a fast archiving of my custom data. However, I don't understand why aSource doesn't respond to the IXRecordManager API. How can I work around this problem?

A: The source inside the IXRecordTranscription protocol methods can either be an IXRecordManager or an IXDataRepository object. In the latter case, the record manager is its delegate. For example, in order to find the record manager, your code should look like this:

- source:aSource willWriteRecord:(unsigned)record
{

/* Check whether aSource is an IXRecordManager */
if (![aSource isMemberOf:[IXRecordManager class]])
aSource = [aSource delegate];

/* aSource is now an IXRecordManager */
/* Process your custom data here */
return self;
}


Valid for 3.0, 3.1, 3.2

Q: When I archive and unarchive an IXStore object, I am able to write it to a typedstream, but reading it back in gives me a memory protection failure with the following backtrace.

Program generated(1): Memory access exception on address 0xe
(protection failure).
0xa025f46 in -[IXStore read:] ()
(gdb) where
#0 0xa025f46 in -[IXStore read:] ()
#1 0x500c9be in InternalReadObject ()
#2 0x500f0f8 in NXReadObject ()

Why?

A: The memory smasher occurs in InternalReadObject() (the archiving code) when that method tries to send an awake message to the unarchived store, which has been freed. The store was freed because it didn't have transactions enabled and hence was in a partially updated state. To fix this problem, you can, for example, call commitTransaction in the write: method of your store object to finish all outstanding transactions before archiving. Note that this is only necessary if transactions are not enabled. If transactions are enabled, the store can be archived with incomplete transactions pending, and reading it back drops the uncommitted changes. See the code snippet below:

/* Make a new storage object with a brand new IXStore */
- init
{
[super init];
storage = [[IXStore alloc] init];
return self;
}

/* Archiving myself */
- read:(NXTypedStream *)stream
{
[super read:stream];
storage = NXReadObject(stream);
return self;
}

- write:(NXTypedStream *)stream
{
[super write:stream];
/* A convenient place to finish outstanding
transactions. Not needed if transactions are
enabled.
*/
[storage commitTransaction];
NXWriteObject(stream, storage);
return self;
}

Please note that this program crasher has been fixed in Release 3.2, and an exception error is raised instead. However, you still need to follow the above guideline to properly archive an IXStore object.

Valid for 3.1, 3.2


MusicKit Questions


Q: As I understand it, the MIDI timer and I/O is handled with the msg_receive call, which is a kind of polling that happens whenever the application has time to poll. But if the application is busy with other things, might not the polling interval become too great or too variable, which could have a musically unacceptable effect?

A: It's true that your application must go into a msg_receive to receive MIDI. However, it's possible to structure your application so that there is a separate Mach thread that does nothing or little else than service this msg_receive. This should provide the response time you need. For example, if you use the MusicKit (see ../NEXTSTEP_Developer/Other_Kits/music_software), there is a feature that allows the MusicKit performance (and, hence, the MIDI msg_receive) to run in a separate thread from the graphics event loop. (This feature is also useful for non-MIDI performances, such as those using DSP synthesis.) Alternatively, if you don't use the MusicKit, you can fork your own thread to handle the MIDI asynchronously to the event loop.

Running the Music Kit in a separate thread makes it unlikely that graphics will prevent you from receiving your MIDI in a timely fashion. However, the MusicKit scheduler (just like the AppKit event loop) is non-preemptive--there's no absolute limit to how much can be done before going back to the msg_receive. Therefore, if you write your own Performer, Instrument, or NoteFilter subclass, make it as efficient as possible, because you won't receive MIDI until you exit your code. In principle, the way to get the fastest MIDI response is to have your own separate thread loop processing the MIDI, without using the MusicKit. However, if that thread does much processing other than receiving MIDI, using the MusicKit would probably be just as efficient (and easier to program). The important thing is to get back to the msg_receive as quickly as possible, whether in the MusicKit, the AppKit, or your own thread.

Valid for 1.0, 2.0, 3.0

Q: What music software is available for NeXT Computers?

A: There is a variety of third party music software available. See the Software and Peripherals Catalog for more details on these programs. You can obtain the catalog by calling 800-848-NeXT in the U.S.

The Music KitÔ is an extensive development tool for programmers and is available in the CCRMA (Stanford's Center for Computer Research in Music and Acoustics) Music and DSP tools distribution. Send mail to musickit@ccrma.stanford.edu for more information. There are some music demos that can be used by non-programmers, such as ScorePlayer and Ensemble. Ensemble combines MIDI in and out with DSP synthesis, soundfile playback, real-time algorithmic composition, interactive note processing, and more.

In the /NextDeveloper/Examples directory, the SoundAndMusic subdirectory contains programs that are runnable by anyone who knows enough Unix to change directories and compile in a shell window.

Some public-domain software is available on the Internet archive servers. Z-quencer is a simple MIDI sequencer with the music displayed in piano-roll style (rather than as music notation). Looching is a program that plays continuous background music on the DSP. Programmers at NeXT created the Lisp Scorefile Package, a Lisp front end for generating Music Kit scorefiles, and RecordApp, a program for sound recording.

Also available for programmers are some environments for software (non-real-time) sound synthesis and soundfile processing, mostly ported from other Unix platforms. Some also do real-time DSP synthesis. Generally these are available with source for a nominal fee or free of charge. CSound was created at M.I.T. and was given a NeXT front end by Pete Yadlowsky (pmy@virginia.edu). F. Richard Moore at U. C. San Diego (frm@sdcarl.ucsd.edu) has ported cmusic, a software synthesis language, and pvoc, a phase vocoder (for sophisticated analysis and resynthesis of recorded sound). Paul Lansky at Princeton ported cmix and rtmix, programs for soundfile processing and mixing. John Rahn at the University of Washington (jrahn@blake.washington.edu) has a Lisp kernel for music composition. Mara Helmuth (mara@woof.columbia.edu) wrote Patchmix, a graphical unit generator patch program that helps you construct synthesis instruments and that writes out cmix code.

At Stanford University, Heinrich Taube has created Common Music, a compositional environment based on the Common Lisp Object System; it creates scorefiles, soundfiles, and DSP synthesis using the Music Kit. Bill Schottstaedt has developed Common Lisp Music, a Lisp-based software synthesis language that uses the DSP for acceleration rather than real-time synthesis. Perry Cook has written SPASM, an interactive vocal synthesis application, ResonatorLab, a real-time digital filter application, and miscellaneous other tools that can be used by non-programmers. Glen Diener is developing a music notation program. Bill Schottstaedt has made available another music notation package, called cmn, in which editing is done from Lisp rather than graphically. The Stanford materials are available by anonymous ftp to ccrma.stanford.edu.

At Northwestern University, Bill Parod (bill_parod@nwu.edu) has created Just, a NeXT Music Kit application for exploring tuning systems and for retuning scorefiles.

There is an extension to TeX for music notation called MuTeX. We are told this works with the NeXT version of TeX, but that it doesn't include 92dpi screen fonts, so you have to print the scores on your printer to see the notation. Presumably someone could use TeX's companion program METAFONT, also bundled on NeXT's 2.0 Extended software release, to create a 92dpi screen font from the provided MuTeX fonts.

Note that there are non-NEXTSTEP programs available for other platforms that might run on NeXT computers under SoftPC or X. For example, SCORE is a powerful music notation program for the IBM PC, and there are some public-domain sound tools that run under X windows.

There is an email-based music interest group that occasionally has announcements of new music-related software for the NeXT. To subscribe, send email to next-music-request@wri.com.

Valid for 1.0, 2.0, 3.0


OpenStep | Alliances | Training | Tech Support | Where to Buy