/* IIWU Synth A soundfont synthesizer * * Copyright (C) 2001 Peter Hanappe * Author: Peter Hanappe, address@hidden * * This file is part of the IIWU program. * IIWU is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. * */ #ifndef _IIWUSYNTH_H #define _IIWUSYNTH_H #include #ifdef __cplusplus extern "C" { #endif #if defined(WIN32) #if defined(IIWUSYNTH_DLL_EXPORTS) #define IIWU_SYNTH_API __declspec(dllexport) #else #define IIWU_SYNTH_API __declspec(dllimport) #endif #else #define IIWU_SYNTH_API #endif /* This is the iiwusynth header and contains the synthesizer's public API. Depending on how you want to use or extend the synthesizer you will need different API functions. You probably do not need all of them. Here is what you might want to do: o Embedded synthesizer: create a new synthesizer and send MIDI events to it. The sound goes directly to the audio out of your system. o Plugin synthesizer: create a synthesizer and send MIDI events but get the audio back into your application. o SoundFont plugin: create a new type of "SoundFont" and allow the synthesizer to load your type of SoundFonts. o MIDI input: Create a MIDI handler to read the MIDI input on your machine and send the MIDI events directly to the synthesizer. o MIDI files: Open MIDI files and send the MIDI events to the synthesizer. o Command lines: You can send textual commands to the synthesizer. */ /* Forward declarations */ typedef struct _iiwu_synth_settings_t iiwu_synth_settings_t; typedef struct _iiwu_synth_t iiwu_synth_t; typedef struct _iiwu_voice_t iiwu_voice_t; typedef struct _iiwu_sfloader_t iiwu_sfloader_t; typedef struct _iiwu_sfont_t iiwu_sfont_t; typedef struct _iiwu_preset_t iiwu_preset_t; typedef struct _iiwu_sample_t iiwu_sample_t; /* * * Synthesizer settings * * * The create a synthesizer object you will have to specify its * settings. These settings are stored in the structure below. It * contains fields for the maximum polyphony, the sample rate, and * so on. * * You should initialize the settings with the IIWU_DEFAULT_SETTINGS * macro as follows: * * void my_synthesizer() * { * iiwu_synth_settings_t settings = IIWU_DEFAULT_SETTINGS; * iiwu_synth_t* synth; * * ... change settings ... * * synth = new_iiwu_synth(&settings); * * ... * * } * * */ /** possible sample formats */ enum { IIWU_FLOAT_FORMAT = 1, IIWU_S16_FORMAT }; /** possible setting flags */ enum { IIWU_AUDIO = 1, IIWU_MIDI = 2, IIWU_REVERB = 4, IIWU_CHORUS = 8, IIWU_VERBOSE = 16 }; struct _iiwu_synth_settings_t { unsigned char version; /** a version number */ unsigned short polyphony; /** maximum polyphony */ unsigned int flags; /** various flags to turn on options */ char* adriver; /** driver name */ char* adevice; /** device name */ unsigned short bufsize; /** the synthesis buffer size */ unsigned short queuesize; /** the audio queue size */ int sample_rate; /** NOT YET USED */ int sample_format; /** currently only float or signed 16 bits */ }; #define IIWU_SETTINGS_VERSION 5 #ifdef WIN32 #define IIWU_DEFAULT_SETTINGS { IIWU_SETTINGS_VERSION, 128, IIWU_AUDIO | IIWU_REVERB, NULL, NULL, 64, 2048, 44100, IIWU_S16_FORMAT } #else #define IIWU_DEFAULT_SETTINGS { IIWU_SETTINGS_VERSION, 128, IIWU_AUDIO | IIWU_REVERB, NULL, NULL, 64, 1024, 44100, IIWU_S16_FORMAT } #endif /* * * Embedded synthesizer * * You create a new synthesizer with new_iiwu_synth() and you destroy * if with delete_iiwu_synth(). Use the settings structure to specify * the synthesizer characteristics. As soon as the synthesizer is * created, it will start playing (it spins of a background audio * thread) and you can immediately start sending MIDI events to it. * * You have to load a SoundFont in order to hear any sound. For that * you use the iiwu_synth_sfload() function. * * The API for sending MIDI events is probably what you expect: * iiwu_synth_noteon(), iiwu_synth_noteoff(), ... * */ /** Creates a new syntehsizer object. As soon as the synthesizer is * created, it will start playing. * * @param settings a pointer to a settings structure * @return a newly allocated synthesizer or NULL in case of error */ IIWU_SYNTH_API iiwu_synth_t* new_iiwu_synth(iiwu_synth_settings_t* settings); /** * Deletes the synthesizer previously created with new_iiwu_synth. * * @param synth the synthesizer object * @return 0 if no error occured, -1 otherwise */ IIWU_SYNTH_API int delete_iiwu_synth(iiwu_synth_t* synth); /* MIDI channel messages */ /** Send a noteon message */ IIWU_SYNTH_API int iiwu_synth_noteon(iiwu_synth_t* synth, int chan, int key, int vel); /** Send a noteoff message */ IIWU_SYNTH_API int iiwu_synth_noteoff(iiwu_synth_t* synth, int chan, int key); /** Send a program change message */ IIWU_SYNTH_API int iiwu_synth_program_change(iiwu_synth_t* synth, int chan, int program); /** Send a control change message */ IIWU_SYNTH_API int iiwu_synth_cc(iiwu_synth_t* synth, int chan, int ctrl, int val); /** Send a pitch bend message */ IIWU_SYNTH_API int iiwu_synth_pitch_bend(iiwu_synth_t* synth, int chan, int val); /** Short-hand functions */ IIWU_SYNTH_API int iiwu_synth_bank_select(iiwu_synth_t* synth, int chan, unsigned int bank); /** Load a SoundFont. The newly loaded SoundFont will be put on top * of the SoundFont stack. Presets are searched starting from the * SoundFont on the top of the stack, working the way down the stack * until a preset is found. */ IIWU_SYNTH_API int iiwu_synth_sfload(iiwu_synth_t* synth, const char* filename); /** Reload a SoundFont */ /* IIWU_SYNTH_API char* iiwu_synth_sfreload(iiwu_synth_t* synth, int num); */ /** Remove a SoundFont for the stack */ /* IIWU_SYNTH_API char* iiwu_synth_sfunload(iiwu_synth_t* synth, int num); */ /** Returns the number of loaded SoundFonts */ IIWU_SYNTH_API int iiwu_synth_sfcount(iiwu_synth_t* synth); /** Returns a loaded SoundFont */ IIWU_SYNTH_API iiwu_sfont_t* iiwu_synth_get_sfont(iiwu_synth_t* synth, int num); /** Get the preset of a channel */ IIWU_SYNTH_API iiwu_preset_t* iiwu_synth_get_channel_preset(iiwu_synth_t* synth, int chan); /** Get a textual representation of the last error */ IIWU_SYNTH_API char* iiwu_synth_error(iiwu_synth_t* synth); /* * * Synthesizer plugin * * * To create a synthesizer plugin, create the synthesizer as * explained above. However, in the settings you have to specify * that no audio output should be created: * * settings.flags &= ~IIWU_AUDIO; * * Once the synthesizer is created you can call any of the functions * below the get the audio. These functions expect two buffers (left * and right channel) that will be filled with samples. * */ IIWU_SYNTH_API int iiwu_synth_write_s16(iiwu_synth_t* synth, int len, void* out1, int loff, int lincr, void* out2, int roff, int rincr); IIWU_SYNTH_API int iiwu_synth_write_float(iiwu_synth_t* synth, int len, void* out1, int loff, int lincr, void* out2, int roff, int rincr); /* Type definition of the synthesizer's audio callback function. */ typedef int (*iiwu_audio_callback_t)(iiwu_synth_t* synth, int len, void* out1, int loff, int lincr, void* out2, int roff, int rincr); /* * * Command line interface * * The command line interface allows you to send simple textual * commands to the synthesizer, to parse a command file, or to read * commands from the stdin. * * To find the list of currently supported commands, please check the * iiwu_cmd.c file. * */ IIWU_SYNTH_API int iiwu_synth_cmdline(iiwu_synth_t* synth, char* cmd, char* reply, int len); IIWU_SYNTH_API int iiwu_synth_cmdfile(iiwu_synth_t* synth, FILE* file); IIWU_SYNTH_API int iiwu_synth_cmdshell(iiwu_synth_t* synth); IIWU_SYNTH_API char* iiwu_get_userconf(char* buf, int len); IIWU_SYNTH_API char* iiwu_get_sysconf(char* buf, int len); /* * * Logging interface * * The default logging function of the iiwusynth prints its messages * to the stderr. The synthesizer uses four level of messages: PANIC, * ERR, WARN, and DBG. They are commented in the definition below. * * A client application can install a new log function to handle the * messages differently. In the following example, the application * sets a callback function to display "PANIC" messages in a dialog, * and ignores all other messages by setting the log function to * NULL: * * ... * iiwu_set_log_function(PANIC, show_dialog); * iiwu_set_log_function(ERR, NULL); * iiwu_set_log_function(WARN, NULL); * iiwu_set_log_function(DBG, NULL); * ... * */ enum iiwu_log_level { PANIC, /* the synth can't function correctly any more */ ERR, /* the synth can function, but with serious limitation */ WARN, /* the synth might not function as expected */ DBG, /* debugging messages */ LAST_LOG_LEVEL }; typedef void (*iiwu_log_function_t)(int level, char* message); /** iiwu_set_log_function installs a new log function for the * specified level. It returns the previously installed function. */ IIWU_SYNTH_API iiwu_log_function_t iiwu_set_log_function(int level, iiwu_log_function_t fun); /** iiwu_default_log_function is the iiwu's default log function. It * prints to the stderr. */ IIWU_SYNTH_API void iiwu_default_log_function(int level, char* message); /* * * SoundFont plugins * * It is possible to add new SoundFont loaders to the * synthesizer. The API uses a couple of "interfaces" (structures * with callback functions): iiwu_sfloader_t, iiwu_sfont_t, and * iiwu_preset_t. * * To add a new SoundFont loader to the synthesizer, you call * iiwu_synth_add_sfloader() and pass a pointer to an * iiwu_sfloader_t structure. The important callback function in * this structure os "load", which should try to load a file and * returns a iiwu_sfont_t structure, or NULL if it fails. * * The iiwu_sfont_t structure contains a callback to obtain the * name of the soundfont. It contains two functions to iterate * though the contained presets, and one function to obtain a * preset corresponding to a bank and preset number. This * function should return an iiwu_preset_t structure. * * The iiwu_preset_t structure contains some functions to obtain * information from the preset (name, bank, number). The most * important callback is the noteon function. The noteon function * should call iiwu_synth_alloc_voice() for every sample that has * to be played. iiwu_synth_alloc_voice() expects a pointer to a * iiwu_sample_t structure and returns a pointer to the opaque * iiwu_voice_t structure. To set or increments the values of a * generator, use iiwu_voice_gen_{set,incr}. When you are * finished initializing the voice call iiwu_voice_start() to * start playing the synthesis voice. * */ /* * Synthesizer's interface to handle SoundFont loaders */ /** Add a SoundFont loader to the synthesizer. Note that SoundFont loader don't necessarily load SoundFonts. They can load any type of wavetable data but export a SoundFont interface. */ IIWU_SYNTH_API void iiwu_synth_add_sfloader(iiwu_synth_t* synth, iiwu_sfloader_t* loader); /** Allocate a synthesis voice. This function is called by a soundfont's preset in responce to a noteon event */ IIWU_SYNTH_API iiwu_voice_t* iiwu_synth_alloc_voice(iiwu_synth_t* synth, iiwu_sample_t* sample, int channum, int key, int vel); /* * The interface to the synthesizer's voices */ /** Set the value of a generator */ IIWU_SYNTH_API void iiwu_voice_gen_set(iiwu_voice_t* voice, int gen, float val); /** Increment the value of a generator */ IIWU_SYNTH_API void iiwu_voice_gen_incr(iiwu_voice_t* voice, int gen, float val); /** Flag the voice as ready for synthesis */ IIWU_SYNTH_API void iiwu_voice_start(iiwu_voice_t* voice); /** List of generator numbers */ enum iiwu_gen_type { GEN_STARTADDROFS, GEN_ENDADDROFS, GEN_STARTLOOPADDROFS, GEN_ENDLOOPADDROFS, GEN_STARTADDRCOARSEOFS, GEN_MODLFOTOPITCH, GEN_VIBLFOTOPITCH, GEN_MODENVTOPITCH, GEN_FILTERFC, GEN_FILTERQ, GEN_MODLFOTOFILTERFC, GEN_MODENVTOFILTERFC, GEN_ENDADDRCOARSEOFS, GEN_MODLFOTOVOL, GEN_UNUSED1, GEN_CHORUSSEND, GEN_REVERBSEND, GEN_PAN, GEN_UNUSED2, GEN_UNUSED3, GEN_UNUSED4, GEN_MODLFODELAY, GEN_MODLFOFREQ, GEN_VIBLFODELAY, GEN_VIBLFOFREQ, GEN_MODENVDELAY, GEN_MODENVATTACK, GEN_MODENVHOLD, GEN_MODENVDECAY, GEN_MODENVSUSTAIN, GEN_MODENVRELEASE, GEN_KEYTOMODENVHOLD, GEN_KEYTOMODENVDECAY, GEN_VOLENVDELAY, GEN_VOLENVATTACK, GEN_VOLENVHOLD, GEN_VOLENVDECAY, GEN_VOLENVSUSTAIN, GEN_VOLENVRELEASE, GEN_KEYTOVOLENVHOLD, GEN_KEYTOVOLENVDECAY, GEN_INSTRUMENT, GEN_RESERVED1, GEN_KEYRANGE, GEN_VELRANGE, GEN_STARTLOOPADDRCOARSEOFS, GEN_KEYNUM, GEN_VELOCITY, GEN_ATTENUATION, GEN_RESERVED2, GEN_ENDLOOPADDRCOARSEOFS, GEN_COARSETUNE, GEN_FINETUNE, GEN_SAMPLEID, GEN_SAMPLEMODE, GEN_RESERVED3, GEN_SCALETUNE, GEN_EXCLUSIVECLASS, GEN_OVERRIDEROOTKEY, /* the initial pitch is not a "standard" generator. It is not * mentioned in the list of generator in the SF2 specifications. It * is used, however, as the destination for the default pitch wheel * modulator. */ GEN_PITCH, GEN_LAST }; /* * iiwu_sfloader_t */ struct _iiwu_sfloader_t { void* data; int (*delete)(iiwu_sfloader_t* loader); iiwu_sfont_t* (*load)(iiwu_sfloader_t* loader, const char* filename); }; /* * iiwu_sfont_t */ struct _iiwu_sfont_t { void* data; int (*delete)(iiwu_sfont_t* sfont); char* (*get_name)(iiwu_sfont_t* sfont); iiwu_preset_t* (*get_preset)(iiwu_sfont_t* sfont, unsigned int bank, unsigned int prenum); void (*iteration_start)(iiwu_sfont_t* sfont); iiwu_preset_t* (*iteration_next)(iiwu_sfont_t* sfont); }; /* * iiwu_preset_t */ struct _iiwu_preset_t { void* data; int (*delete)(iiwu_preset_t* preset); char* (*get_name)(iiwu_preset_t* preset); int (*get_banknum)(iiwu_preset_t* preset); int (*get_num)(iiwu_preset_t* preset); /** handle a noteon event. */ int (*noteon)(iiwu_preset_t* preset, iiwu_synth_t* synth, int chan, int key, int vel); }; /* * iiwu_sample_t */ struct _iiwu_sample_t { char name[21]; unsigned int start; unsigned int end; unsigned int loopstart; unsigned int loopend; unsigned int samplerate; int origpitch; int pitchadj; int cents; int sampletype; int valid; /* the value should be 1. this field will be removed. */ short* data; }; /** Sample types */ #define IIWU_SAMPLETYPE_MONO 1 #define IIWU_SAMPLETYPE_RIGHT 2 #define IIWU_SAMPLETYPE_LEFT 4 #define IIWU_SAMPLETYPE_LINKED 8 #define IIWU_SAMPLETYPE_ROM 0x8000 /* * * MIDI files * * The MIDI player allows you to play MIDI files with the IIWU Synth * */ typedef struct _iiwu_player_t iiwu_player_t; IIWU_SYNTH_API iiwu_player_t* new_iiwu_player(iiwu_synth_t* synth); IIWU_SYNTH_API int delete_iiwu_player(iiwu_player_t* player); IIWU_SYNTH_API int iiwu_player_load(iiwu_player_t* player, char* midifile); IIWU_SYNTH_API int iiwu_player_play(iiwu_player_t* player); IIWU_SYNTH_API int iiwu_player_stop(iiwu_player_t* player); IIWU_SYNTH_API int iiwu_player_join(iiwu_player_t* player); IIWU_SYNTH_API int iiwu_player_set_loop(iiwu_player_t* player, int loop); IIWU_SYNTH_API int iiwu_player_set_midi_tempo(iiwu_player_t* player, int tempo); IIWU_SYNTH_API int iiwu_player_set_bpm(iiwu_player_t* player, int bpm); /* * * MIDI input * * The MIDI handler forwards incoming MIDI events to the synthesizer * */ typedef struct _iiwu_midi_handler_t iiwu_midi_handler_t; /** Create a new midi handler. A midi handler connects to a midi input * device and forwards incoming midi events to the synthesizer. */ IIWU_SYNTH_API iiwu_midi_handler_t* new_iiwu_midi_handler(iiwu_synth_t* synth, char* driver, char* device, char* id); /** Delete the midi handler. * * @param handler a pointer to the midi handler * @return 0 if no error occured, -1 otherwise */ IIWU_SYNTH_API int delete_iiwu_midi_handler(iiwu_midi_handler_t* handler); /* * The following set of functions are an interface to monitor and * debug the midi input */ /** Get the name of the currently used midi driver * * @param handler a pointer to the midi handler * @return the name of the midi driver */ IIWU_SYNTH_API char* iiwu_midi_handler_get_driver_name(iiwu_midi_handler_t* handler); /** Get the name of the currently used midi device * * @param handler a pointer to the midi handler * @return the name of the midi device */ IIWU_SYNTH_API char* iiwu_midi_handler_get_device_name(iiwu_midi_handler_t* handler); /** Get the id of the currently used midi device * * @param handler a pointer to the midi handler * @return the id of the midi device */ IIWU_SYNTH_API char* iiwu_midi_handler_get_id(iiwu_midi_handler_t* handler); /** Get the status midi handler * * @param handler a pointer to the midi handler * @return a string message with the status information */ IIWU_SYNTH_API char* iiwu_midi_handler_get_status(iiwu_midi_handler_t* handler); /** Get the event count for a particular midi event type. The type is * specified by the midi opcode. * * @param handler a pointer to the midi handler * @param type the type of the event queried * @return the number of events occured */ IIWU_SYNTH_API int iiwu_midi_handler_get_event_count(iiwu_midi_handler_t* handler, int type); /* * * Utility functions */ /** * iiwu_is_soundfont returns 1 if the specified filename is a * soundfont. It retuns 0 otherwise. The current implementation only * checks for the "RIFF" header in the file. It is useful only to * distinguish between SoundFonts MIDI files. */ IIWU_SYNTH_API int iiwu_is_soundfont(char* filename); /** * iiwu_is_midifile returns 1 if the specified filename is a MIDI * file. It retuns 0 otherwise. The current implementation only checks * for the "MThd" header in the file. */ IIWU_SYNTH_API int iiwu_is_midifile(char* filename); #ifdef WIN32 /** Set the handle to the instance of the application on the Windows platform. The handle is needed to open DirectSound. */ IIWU_SYNTH_API void iiwu_set_hinstance(void* hinstance); #endif #ifdef __cplusplus } #endif #endif /* _IIWUSYNTH_H */