#include #include #include #include #include #include #include "../include/string.h" #include "../include/disk.h" #include "../include/prochandle.h" #include "midi.h" #include "raw.h" #include "wav.h" #include "voc.h" #include "midiiow.h" #include "afw.h" #include "options.h" static void AFWCopyDataAlloc( const void *data, int len, SoundBuffer **prev_data, YDataLength *prev_len ); int AFWOpen(const char *path, AFWDataStruct *af_data); int AFWLoadSegment( AFWDataStruct *af_data, YDataPosition position, YDataLength length, Audio *audio ); void AFWClose(AFWDataStruct *af_data, Audio *audio); #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define ABSOLUTE(x) (((x) < 0) ? ((x) * -1) : (x)) /* * Coppies the data to the previous data pointer. * Reallocating it as needed. * * prev_data and prev_len are assumed valid. */ static void AFWCopyDataAlloc( const void *data, int len, SoundBuffer **prev_data, YDataLength *prev_len ) { if((data == NULL) || (len < 1) ) { if((*prev_data) != NULL) { free(*prev_data); (*prev_data) = NULL; } (*prev_len) = 0; return; } else { if((*prev_len) != len) { (*prev_len) = len; (*prev_data) = (SoundBuffer *)realloc( *prev_data, (*prev_len) * sizeof(SoundBuffer) ); if((*prev_data) == NULL) { (*prev_len) = 0; return; } } memcpy( *prev_data, /* Destination. */ data, /* Source. */ (*prev_len) * sizeof(SoundBuffer) ); } return; } /* * Reads the header of an audio file specified by path * and puts its parameters and type data in the buffer * pointed at af_data. */ int AFWOpen(const char *path, AFWDataStruct *af_data) { FILE *fp; raw_data_struct rd, *rd_ptr; wav_data_struct wd, *wd_ptr; voc_data_struct vd, *vd_ptr; MidiDataStruct md, *md_ptr; struct stat stat_buf; if((path == NULL) || (af_data == NULL)) return(-1); /* Reset afw data structure. */ memset(af_data, 0x00, sizeof(AFWDataStruct)); /* Open file. */ fp = fopen(path, "rb"); if(fp == NULL) return(-1); /* Get file statistics. */ if(fstat(fileno(fp), &stat_buf)) return(-1); /* The object cannot be a directory. */ if(S_ISDIR(stat_buf.st_mode)) return(-1); /* Try passing the opened fp to each of the library read * header functions. If any of the header functions return * success then that is the right one to use because the * file types were detected to match. * * Also on success we should not referance our fp again. */ /* Is it a WAV file? */ if(WavReadHeader(path, fp, &wd) == WavSuccess) { fp = NULL; /* Do not referance the fp again. */ /* Load as a WAV file. */ af_data->ptr = calloc(1, sizeof(wav_data_struct)); if(af_data->ptr == NULL) return(-1); else wd_ptr = (wav_data_struct *)af_data->ptr; memcpy(wd_ptr, &wd, sizeof(wav_data_struct)); /* Copy other data from library specific structure to * our afw data structure. */ af_data->file_format = AFWFileFormatWav; af_data->sndobj_format = SndObjTypeDSP; af_data->entire_length = wd.data_chunk_size; af_data->buffer = NULL; af_data->length = 0; af_data->block_align = wd.block_align; af_data->block_length = ((wd.bits_per_sample == 16) ? 2 : 1 ); af_data->sample_size = wd.bits_per_sample; af_data->channels = wd.channels; af_data->sample_rate = wd.samples_per_sec; af_data->bytes_per_sec = wd.bytes_per_sec; } /* Is it a VOC file? */ else if(VocReadHeader(path, fp, &vd) == VocSuccess) { int i; fp = NULL; /* Do not referance the fp again. */ /* Load as a VOC file. */ af_data->ptr = calloc(1, sizeof(voc_data_struct)); if(af_data->ptr == NULL) return(-1); else vd_ptr = (voc_data_struct *)af_data->ptr; memcpy(vd_ptr, &vd, sizeof(voc_data_struct)); /* Copy other data from library specific structure to * our afw data structure. */ af_data->file_format = AFWFileFormatVoc; af_data->sndobj_format = SndObjTypeDSP; af_data->entire_length = vd.total_data_len; af_data->buffer = NULL; af_data->length = 0; /* Get block align and length. */ af_data->block_align = 0; af_data->block_length = 1; for(i = 0; i < vd.total_datablocks; i++) { if(vd.datablock[i] == NULL) continue; if(vd.datablock[i]->type != VocBlockTypeSoundData) continue; af_data->block_align = 0; af_data->block_length = ((vd.datablock[i]->bits == 16) ? 2 : 1); af_data->sample_size = 8; af_data->channels = 1; af_data->sample_rate = vd.datablock[i]->sample_rate; af_data->bytes_per_sec = af_data->sample_rate; } } /* Is it a MIDI file? */ else if(MidiRead(path, fp, &md) == MidiSuccess) { fp = NULL; /* Do not referance the fp again. */ /* Load as a MIDI file. */ af_data->ptr = calloc(1, sizeof(MidiDataStruct)); if(af_data->ptr == NULL) return(-1); else md_ptr = (MidiDataStruct *)af_data->ptr; memcpy(md_ptr, &md, sizeof(MidiDataStruct)); /* Copy other data from library specific structure to * our afw data structure. */ af_data->file_format = AFWFileFormatMidi; af_data->sndobj_format = SndObjTypeMIDI; af_data->entire_length = 0; af_data->buffer = NULL; af_data->length = 0; af_data->block_align = 0; af_data->block_length = 0; af_data->sample_size = 0; af_data->channels = 0; af_data->sample_rate = 0; af_data->bytes_per_sec = 0; } /* Is it a RAW file? (This needs to be checked last). */ else if(RawReadHeader(path, fp, &rd) == RawSuccess) { fp = NULL; /* Do not referance the fp again. */ /* Load as a RAW file. */ af_data->ptr = calloc(1, sizeof(raw_data_struct)); if(af_data->ptr == NULL) return(-1); else rd_ptr = (raw_data_struct *)af_data->ptr; memcpy(rd_ptr, &rd, sizeof(raw_data_struct)); /* Copy other data from library specific structure to * our afw data structure. */ af_data->file_format = AFWFileFormatRaw; af_data->sndobj_format = SndObjTypeDSP; af_data->entire_length = rd.data_length; af_data->buffer = NULL; af_data->length = 0; af_data->block_align = rd.block_align; af_data->block_length = (rd.sample_size == 16) ? 2 : 1; af_data->sample_size = rd.sample_size; af_data->channels = rd.channels; af_data->sample_rate = rd.sample_rate; af_data->bytes_per_sec = rd.sample_rate * ((rd.sample_size == 16) ? 2 : 1); } /* If fp is not NULL, then that implies it did not match * any file type supported. */ if(fp != NULL) { fclose(fp); return(-1); } else { return(0); } } /* * If af_data has a sndobj_format of SndObjTypeDSP, then this * function: * * Frees buffer on af_data (if allocated), reads a segment of * data from the DSP sound object on file, and then allocates * a new buffer and updates values on af_data. Returns 0 on success * or -1 on error. * * * If af_data has a sndobj_format of SndObjTypeMIDI, then this * function: * * Checks if the MIDI sound object is being played; If the * sound object is not being played then it forks off a * process to control the MIDI device to play the MIDI sound * object on file, returning: * 0 on success * -1 on error * -2 another play process started thus stopping this one * -3 finished play * If the MIDI sound object is last known to be playing (marked * by a previous call to this function), then it checks if the * child process playing the MIDI sound object is still playing * it, if it has stopped playing then -3 is returned or if it * is still playing then 0 is returned. */ int AFWLoadSegment( AFWDataStruct *af_data, YDataPosition position, YDataLength length, Audio *audio ) { int status; pid_t pid; raw_data_struct *raw_data; wav_data_struct *wav_data; voc_data_struct *voc_data; MidiDataStruct *midi_data; MIDIAudio *midi_audio_ptr = NULL; if(af_data == NULL) return(-1); if(af_data->ptr == NULL) return(-1); /* Check sound object format type. */ if(af_data->sndobj_format == SndObjTypeMIDI) { /* It is a MIDI Sound Object. */ status = 0; /* Get pointer to MIDI Audio. */ if(audio == NULL) { status = -1; return(status); } else { midi_audio_ptr = &audio->midi_audio; } /* Handle by MIDI file format. */ switch(af_data->file_format) { case AFWFileFormatMidi: midi_data = (MidiDataStruct *)af_data->ptr; /* Get pid of actual current MIDI play (if any). */ pid = MIDIIOWIsPlaying(midi_audio_ptr); /* Was this MIDI being played? */ if(af_data->pid > 0) { /* MIDI play is _expected_ to be currently active. */ /* Is a MIDI currently being played? */ if(pid > 0) { /* MIDI play is active but is it the same play * as this MIDI object? */ if(pid != af_data->pid) { /* No it is not the same play, so mark this * as stopped. */ status = -2; af_data->pid = 0; } } else { /* Midi play process has finished. */ status = -3; af_data->pid = 0; } } else { /* This MIDI was not being played, so we are going to * start playing this MIDI. */ /* Is a MIDI currently being played? */ if(pid > 0) { /* An MIDI is still being played, stop it. */ MIDIIOWStop(midi_audio_ptr); pid = 0; } /* Begin new midi play. */ if(midi_data->filename != NULL) { pid = MIDIIOWPlay( midi_audio_ptr, midi_data->filename ); if(pid == 0) status = -1; /* Record new pid. */ af_data->pid = pid; } } break; default: status = -1; break; } return(status); } else if(af_data->sndobj_format == SndObjTypeDSP) { /* It is a DSP Sound Object. */ /* Do not free the buffer on our af_data, it is shared and * will be free'ed by the call to the library function. */ af_data->length = 0; /* Load audio segment by the sound object file format. */ switch(af_data->file_format) { case AFWFileFormatRaw: raw_data = (raw_data_struct *)af_data->ptr; status = RawReadPartialData( raw_data, position, length, RawReadSigned8 ); if(status == RawSuccess) { AFWCopyDataAlloc( raw_data->data, raw_data->data_len, &af_data->buffer, &af_data->length ); } else { af_data->buffer = NULL; } break; case AFWFileFormatWav: wav_data = (wav_data_struct *)af_data->ptr; status = WavReadPartialData( wav_data, position, /* Position. */ length, /* How much to load. */ WavReadSigned8 ); if(status == WavSuccess) { AFWCopyDataAlloc( wav_data->data, wav_data->data_len, &af_data->buffer, &af_data->length ); } else { af_data->buffer = NULL; } break; case AFWFileFormatVoc: voc_data = (voc_data_struct *)af_data->ptr; status = VocReadPartialData( voc_data, position, length, VocReadSigned8 ); if(status == VocSuccess) { AFWCopyDataAlloc( voc_data->data, voc_data->data_len, &af_data->buffer, &af_data->length ); } else { af_data->buffer = NULL; } break; case AFWFileFormatMP3: return(-1); break; default: return(-1); break; } } else { /* Unsupported sound object type. */ return(-1); } return(0); } /* * Closes the audio file, deallocating any allocated resources. * * If the object was a MIDI, then the MIDI play process will * be stopped. */ void AFWClose(AFWDataStruct *af_data, Audio *audio) { pid_t pid; MIDIAudio *midi_audio_ptr; if(af_data == NULL) return; /* Deallocate library resources and memory. */ switch(af_data->file_format) { case AFWFileFormatRaw: RawDestroyData((raw_data_struct *)af_data->ptr); break; case AFWFileFormatWav: WavDestroyData((wav_data_struct *)af_data->ptr); break; case AFWFileFormatVoc: VocDestroyData((voc_data_struct *)af_data->ptr); break; case AFWFileFormatMP3: break; case AFWFileFormatMidi: /* Get pointer to MIDI Audio. */ if(audio != NULL) { midi_audio_ptr = &audio->midi_audio; /* Stop MIDI play if it is playing this. */ pid = MIDIIOWIsPlaying(midi_audio_ptr); if((af_data->pid == pid) && (af_data->pid > 0) ) { /* MIDI play is still playing this MIDI, stop it. */ MIDIIOWStop(midi_audio_ptr); } } /* Reset pid. */ af_data->pid = 0; MidiDestroy((MidiDataStruct *)af_data->ptr); break; default: break; } /* Free library structure (library does not free this). */ free(af_data->ptr); af_data->ptr = NULL; /* Free loaded DSP data, since it's a duplicate of the data * loaded by the library. */ free(af_data->buffer); af_data->buffer = NULL; af_data->length = 0; af_data->block_align = 0; af_data->block_length = 1; af_data->pid = 0; return; }