audacia/lib-src/portburn/portburn_staging.c

298 lines
6.2 KiB
C

/*
* PortBurn
* Common utilities for staging audio files in a temporary directory.
*
* Dominic Mazzoni
* License: LGPL
*
* A library for cross-platform audio CD burning
*
* On some of the platforms supported by PortBurn, we need to stage
* the audio data ourselves. This file defines an interface to the
* common functions.
*/
#include "portburn_staging.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#define swap_uint16(x) \
((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#define swap_uint32(x) \
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
inline const unsigned int FourCharInt32(const char *str) {
return swap_uint32(*(const unsigned int *)(str));
}
typedef struct {
int ntracks;
int cur;
FILE *curfp;
char *tmpdir;
char *filename[99];
char *trackname[99];
int frames[99];
int needswap;
} StagingHandle;
static void PortBurn_Put32(StagingHandle *handle, unsigned int data)
{
if (handle->needswap)
data = swap_uint32(data);
fwrite((void *)&data, 4, 1, handle->curfp);
}
static void PortBurn_Put16(StagingHandle *handle, unsigned short data)
{
if (handle->needswap)
data = swap_uint16(data);
fwrite((void *)&data, 2, 1, handle->curfp);
}
void *PortBurn_TempDirStaging(const char *temporary_directory) {
StagingHandle *h;
volatile unsigned int swaptest;
FILE *fp;
char *tmp2;
char *tmp;
int len;
int i;
if (temporary_directory == NULL)
return NULL;
len = strlen(temporary_directory);
if (len < 2)
return NULL;
tmp = (char *)malloc(len+1);
tmp2 = (char *)malloc(len+6);
strcpy(tmp, temporary_directory);
/* Make all of the parent directories, in case they don't exist. */
for(i=1; i<len; i++) {
if (tmp[i]=='/') {
tmp[i] = 0;
mkdir(tmp, 0777);
tmp[i] = '/';
}
}
mkdir(tmp, 0777);
/* The path shouldn't end in a '/' */
if (tmp[len-1] == '/') {
tmp[len-1] = 0;
len--;
}
/* Make sure we can write and read files in this directory */
sprintf(tmp2, "%s/test", tmp);
fp = fopen(tmp2, "w");
if (!fp) {
free(tmp);
free(tmp2);
return NULL;
}
fprintf(fp, "test");
fclose(fp);
fp = fopen(tmp2, "r");
if (!fp) {
unlink(tmp2);
free(tmp);
free(tmp2);
return NULL;
}
fclose(fp);
unlink(tmp2);
h = (StagingHandle *)malloc(sizeof(StagingHandle));
memset(h, 0, sizeof(StagingHandle));
h->tmpdir = tmp;
free(tmp2);
swaptest = 0x01020304;
if (((unsigned char *)&swaptest)[0] == 1)
h->needswap = 1;
else
h->needswap = 0;
h->cur = -1;
return h;
}
int PortBurn_StartStagingTrack(void *handle, const char *name, int frames)
{
StagingHandle *h = (StagingHandle *)handle;
if (!h)
return -1;
if (h->cur != -1)
return -1;
if (name == NULL)
return -1;
if (h->ntracks == 99)
return -1;
h->cur = h->ntracks;
h->trackname[h->cur] = (char *)malloc(strlen(name)+1);
strcpy(h->trackname[h->cur], name);
h->filename[h->cur] = (char *)malloc(strlen(h->tmpdir)+14);
sprintf(h->filename[h->cur], "%s/track%03d.wav", h->tmpdir, h->cur+1);
h->curfp = fopen(h->filename[h->cur], "wb");
if (!h->curfp) {
free(h->trackname[h->cur]);
free(h->filename[h->cur]);
h->cur = -1;
return -1;
}
h->frames[h->cur] = frames;
struct WavFormatChunk {
unsigned short format;
unsigned short channels;
unsigned int samplerate;
unsigned int byterate;
unsigned short blockAlign;
unsigned short bitsPerSample;
};
PortBurn_Put32(h, FourCharInt32("RIFF"));
PortBurn_Put32(h, 44 + (4 * 1176 * frames));
PortBurn_Put32(h, FourCharInt32("WAVE"));
PortBurn_Put32(h, FourCharInt32("fmt "));
PortBurn_Put32(h, 16);
PortBurn_Put16(h, 1); /* format */
PortBurn_Put16(h, 2); /* channels */
PortBurn_Put32(h, 44100); /* samplerate */
PortBurn_Put32(h, 176400); /* byterate */
PortBurn_Put16(h, 4); /* block align */
PortBurn_Put16(h, 16); /* bits per sample */
PortBurn_Put32(h, FourCharInt32("data"));
PortBurn_Put32(h, 4 * 1176 * frames);
if (ferror(h->curfp)) {
/* Finish this track and let the cleanup function handle it */
fclose(h->curfp);
h->ntracks++;
h->cur = -1;
return -1;
}
return 0;
}
int PortBurn_AddStagingFrame(void *handle, short *buffer)
{
StagingHandle *h = (StagingHandle *)handle;
int i;
if (!h)
return -1;
if (h->cur != h->ntracks)
return -1;
if (!buffer)
return -1;
for(i = 0; i < 1176; i++) {
PortBurn_Put16(h, buffer[i]);
}
if (ferror(h->curfp)) {
/* Finish this track and let the cleanup function handle it */
fclose(h->curfp);
h->ntracks++;
h->cur = -1;
return -1;
}
return 0;
}
int PortBurn_EndStagingTrack(void *handle)
{
StagingHandle *h = (StagingHandle *)handle;
if (!h)
return -1;
if (h->cur != h->ntracks)
return -1;
fclose(h->curfp);
h->ntracks++;
h->cur = -1;
return 0;
}
int PortBurn_FinishStaging(void *handle)
{
return 0;
}
int PortBurn_GetNumStagedTracks(void *handle)
{
StagingHandle *h = (StagingHandle *)handle;
if (!h)
return -1;
return h->ntracks;
}
const char *PortBurn_GetStagedFilename(void *handle, int index)
{
StagingHandle *h = (StagingHandle *)handle;
if (!h)
return NULL;
return h->filename[index];
}
const char *PortBurn_GetStagedTrackName(void *handle, int index)
{
StagingHandle *h = (StagingHandle *)handle;
if (!h)
return NULL;
return h->trackname[index];
}
int PortBurn_GetStagedLengthInFrames(void *handle, int index)
{
StagingHandle *h = (StagingHandle *)handle;
if (!h)
return -1;
return h->frames[index];
}
void PortBurn_CleanupStaging(void *handle)
{
StagingHandle *h = (StagingHandle *)handle;
int i;
if (!h)
return;
free(h->tmpdir);
for(i = 0; i < h->ntracks; i++) {
unlink(h->filename[i]);
free(h->filename[i]);
free(h->trackname[i]);
}
}