* Home * SlinkP Software Projects
Last updated Jun 28, 2008 1:07 pm GMT-5

Paul Winkler

slinkp23@yahoo.com

contents


NOTE: I have sold my SU700 and won't be working on it anymore. I leave this stuff here for the world to use or ignore as it pleases.

The (Abandoned) Goal

I wanted to eventually be able to pop an SU700 floppy or zip disk into my PC and do things with it that I can't do on the SU, saving useful changes in a format the SU can read without having to load samples individually or import them. So the disk would work exactly like one created on the SU. My wish list, in the order I planned to tackle them:

  1. Edit and process samples on the PC using any program, then save them on floppies in SU format
  2. Create sample pad maps on the PC (i.e., which sample goes where)
  3. Copy songs (collections of samples and sequence data) from one volume to another
  4. Use zips in the PC as easily as floppies

The Plan

The big stumper is how to deal with the proprietary filesystem that the SU700 uses for zip disks. I don't know anything about it yet except that it may be the same as the one Yamaha uses for the A3000 sampler. To really do all this right, we'd need to reverse-engineer the filesystem. Ugh. In the meantime, everything else can at least be tested and used with floppies.

To start with, we need to know the sample format and how the samples are mapped to the SU's pads. This is going well. I got part-way through a hack to SoX that would allow it to read and write the SU's files. Then I realized that the unusual way the SU files handle stereo made it just about impossible to do stereo in SoX. So instead I've written a little program in Python that does the job. It seems to be working. Needs more testing.

I will probably NOT attempt to do anything useful with the song sequence data file (except copy it between volumes). This stores every kind of event the SU can record, so it's a complex binary file and would need a complex application to really do anything useful with it. Just deciphering it would be hard (for me at least, I'd be really psyched if someone else did it!)

My SoX module

Originally I was working on a SoX module to support read / write of the SU700 format so we can convert to / from other sample formats. It sort of kind of worked. I'll leave it here in case any good C programmer wants to take another crack at it. I don't plan to work on this any more, it's too frustrating. You will also need the SoX source code from sox.sourceforge.net.

How far did I get? Reading ssp files with my hacked SoX seems to work OK; writing sort of works but the SU has problems with loading the resulting files. Some samples don't load at all- I don't know why yet - while others load happily but fill up all of the SU's memory until you do sample-> process -> trim. I never could think of a way to get SoX to do non-interleaved stereo.

My Python Script

I've written a python module based on the aif.py and wav.py modules that come with Python 1.5.2. It now seems to work; I can save stereo SSP files that the SU700 will load just fine. The sample-swapping problem is taken care of.

PLEASE NOTE that this is an alpha release of Free Software. There is NO WARRANTY OF ANY KIND. Use at your own risk.

Also note that currently the script is very, very slow. This will improve dramatically - I know exactly what I did to slow it down, I just haven't bothered to fix it yet.

You can get my script here. This is a zip file containing the ssp.py module and the ssptool.py script. To run these, you must first install Python. Python is a programming language that runs on mac, pc, or unix / linux. Please don't ask me how to install python, or how to run a python program. Those questions are answered thoroughly on the python website. If you can't figure it out for yourself, I honestly don't have time to help you right now. I will eventually release a more user-friendly program that you can just install and use without fuss.

How You Can Help

OK, I think I've cracked all the mysteries of the ssp format. But I've done very little testing of my latest hacks, so this needs verification. Try my program and see if you have files that don't work with it.

The ssp format is based on AIFF, but there are at least two *very* weird features of ssp samples, compared to the aiff format. As a result, I've given up on SoX and instead have written a Python program to convert to / from SSP.

The weird things are:

  1. Samples are swapped. This is NOT the usual endianness issue - it's WORD-swapping. Like, if you import a sample with frames a, b, c, d, e, f, the SU700 file will re-order them as b, a, d, c, f, e. If this isn't taken into account, the sounds have a lot of high-frequency distortion.
  2. Stereo channels are NOT interleaved, they're written into two separate chunks. This prevents me from writing useable stereo SSP files in SoX.

SU Volume Structure

When an SU700 volume is saved onto a DOS floppy, there are three kinds of files saved. They have the extensions .dat, .ssq, and .ssp.

There is only one .dat file per volume, called songcont.dat; but I think there is one .ssq file for each song (I haven't looked at multi-song volumes yet).


SONGS

songcont.dat is basically an ASCII file, though you can't read it with a typical text editor because it's full of NULL characters.

It describes the sample map for all songs in the volume. Looks quite straightforward. If I'm right, it should be a simple matter to write a utility that changes song and sample names; adds, deletes, and re-maps samples; and copies samples from one song to another. Of course this won't change the song sequence data at all, so it won't be possible to do radical re-maps with existing sequences or you'll get all the wrong sounds going. But it should be simple to create a utility that allows us to do things like pull out e.g a snare drum and drop in a different one. It's just a matter of copying the sound to an .ssp file with the right name and putting that name in the right spot in songcont.dat.

I wonder if you could even trick the SU into using the same sample in different songs? --NO. I thought maybe it could be fooled into loading the same sample twice; but I tried setting the same sample name in two positions in songcont.dat, and apparently this is ignored - the su700 just loaded the normal samples for those positions. However, giving a name in a non-standard format (e.g. test1 instead of s01fr01) causes the SU to give up with "FILE NOT FOUND" error. So no fancy sample names, alas.

Looking at an octal dump of songcont.dat, I can see the following structure:

It's divided into 20 songs, each of 370 bytes. So this file should always be exactly 7400 bytes long.

Each song has the following structure:

     song name
     sample name for loop 01
     sample name for loop 02
     ...
     sample name for loop 08
     sample name for composed loop 01
     sample name for composed loop 02
     ...
     sample name for composed loop 16
     sample name for free pad 01
     sample name for free pad 02
     ...
     sample name for free pad 16

All of these names are represented by 8 ASCII characters, terminated by the NULL character, so there are 9 bytes per name. If a pad has no sample assigned to it, the name is all NULL characters.

So in each song there are (1 + 8 + 16 + 16) * 9 = 369 bytes .... I think there's an extra NULL character between songs making the total 370 which is what it looks like.

(to test that, put a sample in free pad 16, save the volume, load it into the computer and see if there's 2 null characters before the next song name. ... OK, did so. Yup, if I load S01FR03 from disk into S01FR16; when I save the volume, it's now listed as S01FR16, not S01FR03. AND it makes the 370th byte into 001 instead of \0. When does that happen, and why? )

So to find e.g. the 2nd composed loop in the 3rd song, you just add up the byte count of whatever you need to skip:

    2 complete songs = 370 * 2 = 740
    1 song name =		   9
    8 loop names = 9 * 8       =  72
    1 composed loop =	           9
    -----------------------------------
    s03cl02 should be at byte	 830

DO THE NAMES MATTER?

The sample names seem to be always the same format, but the song name does indeed change if you set a name for the song. Likewise, the name of the .ssq file changes to match.

I tried setting the same sample name in two positions in songcont.dat, but apparently this is ignored - the su700 just loaded the normal samples for those positions.

I also tried renaming a sample and changing its name in the songcont.dat, but the SU said "file not found".



SAMPLE FORMAT

The SSP Format

SU700 sounds are stored in a proprietary "ssp" format. Looking at some SU700 samples, here's what I notice. the unix "file" command thinks they are AIFF files, so I try renaming them to .aiff to see if other programs agree. sox won't play them as AIFFs, but sndinfo can get some useful information from the header.

Sndinfo doesn't get the no. of sample frames right - it's always "-1073745336 (null)" samples. But the sampling rate, number of channels, and bit depth seem to be correct.

So it looks like we're dealing with a modified AIFF format.

I just noticed - the .ssp files are ALWAYS an even multiple of 512 bytes long. The header is apparently right around 512 bytes, too. (though I'm not 100% sure of where the sound data actually starts) Obviously the SU OS likes the number 512. Maybe this makes memory addressing easier for them.

Most of the test files are mono, 16-bit, 44100 samples. So playing the files with an unpatched version of sox, the samples mostly sound right if I do: sox -t raw -c 1 -r 44100 -w -x -s foo.ssp -t ossdsp /dev/audio ...where

       -c 1 = mono
       -r 44100 = sampling rate
       -w = "word" (2 bytes)
       -x = swap bytes (this is in big-endian order, like AIFF)
       -s = signed integer samples

... and the rest just means use the OSS output driver.

Some of the sounds seem to cut off abruptly when played on the computer, I think probably because they weren't trimmed and the end point marker is not being taken into account.

Sndinfo complains "unknown chunk APPL of size..." ...which I think is the actual sample data since it seems to be of about the right size, and there are two APPL chunks in the stereo file. I think that's a bug in sndinfo since APPL is part of the AIF-C standard.

I'm now looking at position of strings within the files; All the strings in the headers are at the same point in every file. Cool - that makes things easier.

AIF-C Chunks

The AIF-C standard (see http://home.sprynet.com/~cbagwell/aiff-c.txt) defines all of these chunks - cool! So it really should be standard AIFF stuff.

Note that some things that look like one string are actually two. That's because AIFF headers don't necessarily end strings with a null character like in C; they use "pstrings" (strings that start with a one-byte length counter, followed by the characters; total including the counter must be an even number of bytes, if necessary padded with a trailing null character),

So here's the chunks I've found, with dots representing the bytes that vary from one soundfile to another:

starting byte 
(octal / decimal)       chunk (and strings that are always in them)
-----------------       -------
0000	  0		FORM....AIFF
0014	 12		COMM......................
0046	 38		MARK.............START POINT.......END POINT
0122     82             NAME....(8-char pad name goes here)
0142	 98		INST........................
0176	126		APPL...zSU7M....

There is no SSND chunk (the AIF standard audio data chunk). Instead, the audio data goes in one or two APPL chunks.

Since the chunks always seem to have the same length here (unlike standard AIFF in which many of them vary), we can check out a given chunk for all files by doing

find . -name "*.ssp" -exec od -c --skip-bytes=X --read-bytes=Y {} \;

... where X is the byte count into the file, and Y is how many bytes total to read. Y is calculated by subtracting X from the next chunk's X.

The order of the files in the examples is given by:

$ find . -name "*.ssp"

s01cl01.ssp
s01cl02.ssp
s01cl03.ssp
s01cl04.ssp
s01fr01.ssp
s01fr02.ssp
s01fr03.ssp
s01fr04.ssp
s01lp01.ssp
s01lp02.ssp

THE CHUNKS with EXAMPLES

FORM

The FORM chunk is a "container" for the rest of the chunks. bytes 00 - 013 (12 bytes total, decimal 0 - 11) It's always like this:
 F O R M \0 . . 0370 A I F F 

...where the dots represents values that change. According to the AIFC standard, the 4 bytes between FORM and AIFF should be a long unsigned int representing the chunk data size in bytes (called ckDataSize in the standard), not counting the 4 bytes used for the chunk ID (in this case FORM) and the 4 bytes for the ckDataSize itself. Unfortunately, I can't seem to confirm this. Shouldn't it then just be the file size - 8 ??? But it's not. Looked at some AIFF files too - not for them either!! I must misunderstand this. -- HEY, it's probably an endianness issue. Look at it again from that perspective. ..... nope. Weird. The ckDataSize is always the size of the file minus a few kbytes. (varies from 1 to 7 kb.) Why?? Anyway, I'm not worrying about this - the SoX code for aiff files seems to work fine.

Why is the last byte of ckDataSize always 0370 (decimal 248)? It's not so for normal AIFF files... might have to do with the fact that the SU likes to save samples with byte lengths in multiples of 512. I can't make that add up, but it must have something to do with it...

Examples:

find . -name "*.ssp" -exec od -c --skip-bytes=0 --read-bytes=12 {} \;
0000000   F   O   R   M  \0  \0   I   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0  \0   1   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0 005   '   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0 001   -   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0 001 031   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0  \0   ¥   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0  \0   ¹   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0 005   S   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0 004   c   ø   A   I   F   F
0000014
0000000   F   O   R   M  \0 002   9   ø   A   I   F   F
0000014

COMM

COMM is standard "common chunk". It "describes fundamental parameters of the sampled sound" such as numChannels, sampleSize, etc. bytes 014 - 045 (26 bytes, from decimal 12 - 37)

Structure in AIFC:

ID ckID; 
long ckDataSize; 
short numChannels; 
unsigned long numSampleFrames; short sampleSize (bit depth); 
extended sampleRate; 
ID compressionType; 
pstring compressionName.

("extended" is an IEEE 80-bit float.)
So that's bytes
12-15: ckID
16-19: ckDataSize (for us this is always 022 which is = 18)
20-21: numChannels (always 01 or 02)
22-25: numSampleFrames
26-27: sampleSize  (always 020 (16) or \b (8)).
28-37: sampleRate 
( 38-41: compressionType ; 42-?: compressionName)
Looks like we just skip the compression parts - no surprise there.

I can't figure out how to understand the 10-byte floats for the sampling rate - od doesn't support it. The 10 bytes shown for every file here should mean 44100. But this is not really a problem as the code SoX uses for AIFF headers seems to work just fine.

examples:

find . -name "*.ssp" -exec od -c --skip-bytes=12 --read-bytes=26 {} \;
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0  \0   #   3  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0  \0 027 177  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0 002 222   z  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0  \0 225   T  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0  \0 213   5  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0  \0   ¢ 217  \0  \b
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0  \0   ¶ 206  \0  \b
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0 002   ¨   É  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 002  \0 001 027   ð  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046
0000014   C   O   M   M  \0  \0  \0 022  \0 001  \0 001 033   >  \0 020
0000034   @ 016   ¬   D  \0  \0  \0  \0  \0  \0
0000046

MARK

MARK is a standard aiff chunk. It contains markers, which is what START POINT and END POINT are for. Bytes 046-0121 (44 bytes, from decimal 38 - 81) Looks like this:
       M A R K   \0 \0 \0 044 \0 002 \0 001  \0 \0 \0 \0 
     013 S T A R  T \s  P   O  I   N  T  \0 002 \0  .  .  .
     011 E N D   \s  P  O   I  N   T

This breaks down as:

  ID (always MARK)
  long ckDataSize (always 36 aka 044)
  unsigned short numMarkers (always 2)
  Marker markers[]


Each marker then breaks down as:

  MarkerId id; /*  must be > 0  */
  unsigned long position; /*  sample frame number  */
  pstring markerName;   /* always \vSTART POINT or \tEND POINT

So from the example above, the 2 markers are:

  000 001               (MarkerID)
  000 000 000 000    (position)
  013 S   T   A   R   T   \s  P  O  I  N  T

  000 002
  .   .   .   .      (position)
  011 E   N   D   \s  P   O   I  N   T

Most of the START POINTs are just set to 0 by default; and that's what I do with writing files from SoX. For END POINT, I set it to the last sample in the file. ... But I think I've been doing this wrong - see the section on the APPL chunk. The samples end before I thought they did.

Looks like sample data is supposed to start at the 513th byte of an ssp file. Sound editors and hex / octal viewing confirm this.

Example MARK chunks:

 find . -name "*.ssp" -exec od -c --skip-bytes=38 --read-bytes=44 {} \;
0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0  \0
0000106   #   .  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0  \0
0000106 027   z  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0 002
0000106 222   u  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0  \0
0000106 225   O  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0  \0
0000106 213   0  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0  \0
0000106   ¢ 212  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0  \0
0000106   ¶ 201  \t   E   N   D       P   O   I   N   T
0000122
0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0 002
0000106   ¨   Ä  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0 001
0000106 027   ë  \t   E   N   D       P   O   I   N   T
0000122

0000046   M   A   R   K  \0  \0  \0   $  \0 002  \0 001  \0  \0  \0  \0
0000066  \v   S   T   A   R   T       P   O   I   N   T  \0 002  \0 001
0000106 033   9  \t   E   N   D       P   O   I   N   T
0000122

NAME

NAME is the standard chunk for the name of the sampled sound. Bytes 0122-0141 (16 bytes, from decimal 82 to 97) Looks like this:
     N A M E \0 \0 \0 010 . . . . . . . .

.... where the 8 variable characters are the sample name, not including ssp, padded with spaces.

THis is a super-simple chunk; just ID, ckDataSize (always 8 aka 010 aka \b - remember this is the size of the pstring), followed by 8-character name).

Examples:

find . -name "*.ssp" -exec od -c --skip-bytes=82 --read-bytes=16 {} \;
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   C   L   0   1    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   C   L   0   2    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   C   L   0   3    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   C   L   0   4    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   F   R   0   1    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   F   R   0   2    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   F   R   0   3    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   F   R   0   4    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   L   P   0   1    
0000142
0000122   N   A   M   E  \0  \0  \0  \b   S   0   1   L   P   0   2    
0000142

INST

INST is standard. This chunk contains info like base pitch, base velocity, which loop is sustain & which release, etc. From decimal 98 to 126 (28 bytes). Looks like this:

     I   N  S  T \0 \0 \0 024 074 \0 \0 0177 001 0177 \0 \0 \0 \0 \0 001 \0
          002 \0 \0 \0 001 \0 002
They all look the same, not surprising since the SU700 does not use these parameters as far as I know.

Examples:

 find . -name "*.ssp" -exec od -c --skip-bytes=98 --read-bytes=28 {} \;
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176
0000142   I   N   S   T  \0  \0  \0 024   <  \0  \0 177 001 177  \0  \0
0000162  \0  \0  \0 001  \0 002  \0  \0  \0 001  \0 002
0000176

APPL

APPL is standard. But contains application-specific data. This could be anything. But it always contains an application signature (in this case, SU7M or SU7R). The SU700 uses APPL chunks to store the sound data.

From the aifc standard, the spec is:

  ID ckID; /*  'APPL'  */
  long ckDataSize;
  OSType applicationSignature;
  char data[];

 For us it looks like:
 
 A P P L \0 . . z S U 7 M \0 . . . 001 p \0 
Note that the application signature is SU7R for the second (right) channel.

The 4-byte chunk data size seems to be accurate, not including itself (i.e. go from just before SU7M until end of file, or until the next APPL chunk if this is a stereo file).

I think the 4 bytes after SU7M give the size, in bytes, of the sample data. In other words, it should be the number of sample frames times the sample resolution (in bytes). Confirmed for stereo 16-bit.

The next 2 bytes (shown here as 001 and 'p') are always 0x170 for the left channel and 0x1ee for the right (decimal 368 and 494 respectively). (VERIFY FOR MORE EXAMPLES of RIGHT CHANNEL AND FOR 8-BIT FILES) This tells us how many bytes to skip ahead to find the sound data: if we go 368 bytes forward from here in the first APPL chunk, we're at the start of the sound data. In between is all null. Likewise for the second channel, after 0x1ee if we go 494 bytes forward we find the sound data. The purpose is to align the start of sound data at a multiple of 512 bytes into the file.

I wondered if any of this had anything to do with the normal AIFC SSND chunk:

ID ("SSND")
ckDataSize (signed long)
offset (unsigned long)
blockSize (unsigned long)
soundData (bytes...)
... but I don't think so.

Examples (first 32 bytes of each):

find . -name "*.ssp" -exec od -c --skip-bytes=126 --read-bytes=32 {} \;
0000176   A   P   P   L  \0  \0   I   z   S   U   7   M  \0  \0   F   h
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0  \0   1   z   S   U   7   M  \0  \0   /  \0
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0 005   '   z   S   U   7   M  \0 005   $   ô
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0 001   -   z   S   U   7   M  \0 001   *   ¨
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0 001 031   z   S   U   7   M  \0 001 026   l
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0  \0   ¥   z   S   U   7   M  \0  \0   ¢ 220
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0  \0   ¹   z   S   U   7   M  \0  \0   ¶ 210
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0 005   S   z   S   U   7   M  \0 005   Q 224
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0 002   1   z   S   U   7   M  \0 002   /   à
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236
0000176   A   P   P   L  \0 002   9   z   S   U   7   M  \0 002   6   |
0000216 001   p  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000236

It's all zeros from 0220 to 0777. Then at 01000 we start seeing stuff that's probably the sample data. There sure are a lot of repeating values in some of the files there.

BUT s01fr02 and s01fr03 (both 8-bit files!) are showing nothing but zeros until...

s01fr02:
0001337    256    257    512    258    257    257   -255      0
0001357   -511     -2  -1025   -772  -2819  -1546  -3845  -3600
0001377  -3342  -3598  -2064  -2570   -780  -1541    504   -257


s01fr03:
0000777      0      0      0      0      0      0      0      0
*
0001177    256      0    768    258    257    514   1027    772
0001217   1026   1541   1285    258   2306   2313   2568   1801

OK, I can look at a sample in snd, zoom in and use the verbose cursor to find the value for each sample. Unfortunately it's normalized to +-1 so I don't know exact integer values... but I can get +/- 1 sample by dividing by 32767.

Let's use that technique to try to find the sound data.

In s01cl01.pps I find this sequence at start of song data: (sample counts are decimal)

starting at sample 255, aka byte 511 (octal 0777) (remember that we count from 0 so this is the 512th byte!)

estimation from looking at the sample:
0	(3-4)	-1	(3-4)	(3-4)	2	(3-4)	(5-6)


od -i --skip-bytes=511 --read-bytes=16 s01cl01.ssp :

0        -253    255     4       4      2        4        5

Looks like a match.

Hum, weird. Why are the first two non-zero values wrong?

The Actual Sound Data

Well, I didn't expect this part of the file to be weird. Ha ha ha.

It looks as if the SU is reversing each pair of *words* (and I DON'T mean bytes). Wow that's bizarre. I compared an aif to a wav of the same sound to make sure I knew what byte-swapping looks like... and this is definitely swapping PAIRS of bytes. AHA, that explains why the high end sounds funny - this is audible as very high-freq distortion. E.g. here's part of the original aiff (viewed in hexl mode in Emacs, wish I'd discovered that the first day I played with this stuff!):

00000050: 0000 0000 031a 0299 063e 0d09 0747 03c3  .........>...G..
00000060: 0abc 15c4 09ed fc23 12db 1a97 043b 01c3  .......#.....;..

And here's the corresponding part of the SU-generated SSP file:

00000200: 0000 0000 0299 031a 0d09 063e 03c3 0747  ...........>...G
00000210: 15c4 0abc fc23 09ed 1a97 12db 01c3 043b  .....#.........;
I haven't looked at what happens with 8-bit files yet.

Then there's the issue of what they do with the data at the end of each file, which fills it out to an even multiple of 512 bytes.

I've discovered that it is, in fact, an excerpt from earlier in the same file. I just can't figure out how they decide where to get it from, since it doesn't seem to be the same point in every file.

Random Gunk

Almost the end of this huge rambling page. In fact you can ignore this bit and stop reading now. Here's the results of "du foo.aiff" followed by "sndinfo foo.aiff" for each file... This is not very interesting actually, but it's one of the first things I did so I left it in here.
20480 s01cl01.aiff

NAME: S01CL01 
unknown chunk APPL of size 18810
s01cl01.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01cl01.aiff:
	srate 44100, monaural, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)



16384	s01cl02.aiff

NAME: S01CL02 
unknown chunk APPL of size 12666
s01cl02.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01cl02.aiff:
	srate 44100, monaural, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


344064	s01cl03.aiff

NAME: S01CL03 
unknown chunk APPL of size 337786
s01cl03.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01cl03.aiff:
	srate 44100, monaural, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


81920	s01cl04.aiff

NAME: S01CL04 
unknown chunk APPL of size 77178
s01cl04.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01cl04.aiff:
	srate 44100, monaural, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


77824	s01fr01.aiff

NAME: S01FR01 
unknown chunk APPL of size 72058
s01fr01.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01fr01.aiff:
	srate 44100, monaural, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


45056	s01fr02.aiff

NAME: S01FR02 
unknown chunk APPL of size 42362
s01fr02.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01fr02.aiff:
	srate 44100, monaural, 8 bit signed chars, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


49152	s01fr03.aiff

NAME: S01FR03 
unknown chunk APPL of size 47482
s01fr03.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01fr03.aiff:
	srate 44100, monaural, 8 bit signed chars, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


356352	s01fr04.aiff

NAME: S01FR04 
unknown chunk APPL of size 349050
s01fr04.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01fr04.aiff:
	srate 44100, monaural, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


294912	s01lp01.aiff

NAME: S01LP01 
unknown chunk APPL of size 143738
unknown chunk APPL of size 143864
s01lp01.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01lp01.aiff:
	srate 44100, stereo, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


151552	s01lp02.aiff

NAME: S01LP02 
unknown chunk APPL of size 145786
s01lp02.aiff: AIFF, -1073745336 (null) samples, baseFrq 261.6 (midi 60), gain 0 db, sustnLp: mode 0, relesLp: mode 0
s01lp02.aiff:
	srate 44100, monaural, 16 bit shorts, 0.00 seconds
	headersiz 0, datasiz 0 (0 sample frames)


     Send me some mail slinkP home page Powered by Zope