Public SiteDocumentation
 

jPatch - Codecs

Introduction

The jPatch API not only has to deal with the in-memory representation of the patch object model but also has to handle different representations of a patch and the necessary transformations.

Requirements

preliminary note: the transformations can be subdivided into two tasks corresponding the tasks of a decoder and an encoder.

  • transformation from an unknown source to the in-memory patch model
  • transformation from the in-memory patch model to a unknown target

usage requirements:

  • an application should be able to handle different source/target representations without knowledge of implementation details.
  • a common case is that the application has to deal with different file formats (or different midi message formats) in both directions (import/export).
    Thus it should be possible to add new encoder and decoder implementations to an application without the need of recompiling the application. This also requires the managment of installed encoders and decoders.
  • extension management: encoders/decoders have to provide information that allowes an application to decide which one can/should be used with a specific synthesizer model/version and patch format/version.

technical requirements:

  • some data sources (especially when the data source are midi messages) only provide portions of the required information. Thus it should be possible to handle the case that not all data is available at once.

Solution

The solution we provide for the previously described requirements uses the mentioned

  • decoder - for the transformation from a specified source to the in-memory patch model
  • encoder - for the transformation from the in-memory patch model to a specified target

Note: it is not necessary to provide a complete codec (encoder/decoder pair).

For representing the source and the target we provide the Source and the Target interface.

known implementations

interface Sourceinterface Targetcomment
FileSourceFileTargetfor reading/writing files
BitStreamSourceBitStreamTargetfor reading/writing bit streams
(Nord Modular implementation)

The decoder/encoder interfaces handle the specified Source/Target implementations:

public interface PatchDecoder 
{
    void decode(Source source) throws
        UnsupportedSourceException // specified source not supported
        PatchDecoderException // an exception while decoding happened
    ;

    Patch getPatch() throws // returns the resulting patch
        PatchDecoderException // probably the result is not available
    ;
	...
}

public interface PatchEncoder
{
    void encode(Target target) throws
        UnsupportedTargetException // specified target not supported
        PatchEncoderException // an exception while encoding happened
    ;
    ...
}

Encoder

The encoder creates a new representation of a patch for a specified target. The target is represented by the interface net.sf.nmedit.jpatch.io.Target.

developing a custom encoder

A custom encoder requires the implementation of

  • net.sf.nmedit.jpatch.spi.PatchEncoderProvider
  • net.sf.nmedit.jpatch.io.PatchEncoder

and a text file named 'net.sf.nmedit.jpatch.spi.PatchEncoderProvider' in the 'META-INF/services' directory containing the class names of the custom PatchEncoderProvider implementations.

See also:

The jar package file hierarchy:

src/
    META-INF/services/
        net.sf.nmedit.jpatch.spi.PatchEncoderProvider
    packagename/encoder/
        MyPatchEncoder.java
        spi/
            MyPatchEncoderProvider.java

text file 'net.sf.nmedit.jpatch.spi.PatchEncoderProvider':

# my patch encoder provider
test.package.encoder.MyPatchEncoderProvider

MyPatchEncoder.java:

package packagename.encoder;
import net.sf.nmedit.jpatch.io.PatchEncoder;
public class MyPatchEncoder implements PatchEncoder
{
	// see javadoc for implementation details
}
	

MyPatchEncoderProvider.java:

package packagename.encoder.spi;
import net.sf.nmedit.jpatch.spi.PatchEncoderProvider;
public class PatchEncoderProvider extends PatchEncoderProvider
{
	// see javadoc for implementation details	
}
	

Example: file reading

import java.io.FileReader;
import java.io.Reader;
	
import net.sf.nmedit.jpatch.io.Source;
import net.sf.nmedit.jpatch.io.FileSource;
import net.sf.nmedit.jpatch.io.PatchEncoder;
import net.sf.nmedit.jpatch.spi.PatchImplementation;
import net.sf.nmedit.jpatch.Patch;
import net.sf.nmedit.jpatch.io.Target;
	
public Patch load(String file) throws 
	PatchDecoderException, // error while encoding
	UnsupportedSourceException, // source not supported
	IOException
{
    String patchFormat = "Clavia Nord Modular Patch";
    String patchVersion = "3.03";
	
    PatchImplementation pImpl = 
    PatchImplementation.getImplementation(patchFormat, patchVersion);
	
    // get encoder 
    PatchDecoder decoder = pImpl.createPatchDecoder(FileSource.class);

	// create file reader
    FileReader fr = new FileReader(file);
	
    // create file source
    Source source = new FileSource(fr);
	
	// read patch file and create patch
    decoder.decode( source );

	// return patch
    return decoder.getPatch();
}	

Decoder

The decoder creates a new patch from a specified source. The source is represented by the interface net.sf.nmedit.jpatch.io.Source.

developing a custom decoder

Similar to the 'developing a custom encoder' description - just substitute encoder with decoder.

Example: file writing

import java.io.FileWriter;
import java.io.Writer;
	
import net.sf.nmedit.jpatch.io.Target;
import net.sf.nmedit.jpatch.io.FileTarget;
import net.sf.nmedit.jpatch.io.PatchEncoder;
import net.sf.nmedit.jpatch.spi.PatchImplementation;
import net.sf.nmedit.jpatch.Patch;
import net.sf.nmedit.jpatch.io.Target;
	
public void save(String file, Patch patch) throws 
	PatchEncoderException, // error while encoding
	UnsupportedTargetException, // target not supported
	IOException
{
	// create encoder for FileTarget
	PatchEncoder encoder =
		patch
		.getPatchImplementation()
		.createPatchEncoder(FileTarget.class);

	// set source patch
	encoder.setSource(patch);
	
	// create writer
	Writer writer = new FileWriter(file);
	
	// create target
	Target target = new FileTarget(writer);
	
	// write file
	enc.encode(target);
                    
}