diff options
author | Marcus Huderle <huderlem@gmail.com> | 2016-09-08 14:05:20 -0700 |
---|---|---|
committer | Marcus Huderle <huderlem@gmail.com> | 2016-09-08 14:06:35 -0700 |
commit | ccdc106333cb669ffa09779a94cc0646ccaea08e (patch) | |
tree | 2f1daf00eaab1d06293e303b185b117ec58bbae6 /tools | |
parent | 949bc718359b40553c5bbf7e04974dc43283a883 (diff) |
Convert .aif files to .pcm samples, and update Makefile to use aif2pcm tool.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/aif2pcm/LICENSE | 19 | ||||
-rw-r--r-- | tools/aif2pcm/Makefile | 15 | ||||
-rw-r--r-- | tools/aif2pcm/extended.c | 171 | ||||
-rw-r--r-- | tools/aif2pcm/main.c | 554 |
4 files changed, 759 insertions, 0 deletions
diff --git a/tools/aif2pcm/LICENSE b/tools/aif2pcm/LICENSE new file mode 100644 index 000000000..60c9e63c4 --- /dev/null +++ b/tools/aif2pcm/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 huderlem + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tools/aif2pcm/Makefile b/tools/aif2pcm/Makefile new file mode 100644 index 000000000..ec6343b12 --- /dev/null +++ b/tools/aif2pcm/Makefile @@ -0,0 +1,15 @@ +CC = gcc + +CFLAGS = -Wall -Wextra -Wno-switch -std=c11 -O2 + +LIBS = -lm + +SRCS = main.c extended.c + +.PHONY: clean + +aif2pcm: $(SRCS) + $(CC) $(CFLAGS) $(SRCS) -o $@ $(LIBS) + +clean: + $(RM) aif2pcm aif2pcm.exe diff --git a/tools/aif2pcm/extended.c b/tools/aif2pcm/extended.c new file mode 100644 index 000000000..067019661 --- /dev/null +++ b/tools/aif2pcm/extended.c @@ -0,0 +1,171 @@ +/* $Id: extended.c,v 1.8 2006/12/23 11:17:49 toad32767 Exp $ */ +/*- + * Copyright (c) 2005, 2006 by Marco Trillo <marcotrillo@gmail.com> + * + * Permission is hereby granted, free of charge, to any + * person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice + * shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS + * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <math.h> +#include <string.h> + +/* + * Infinite & NAN values + * for non-IEEE systems + */ +#ifndef HUGE_VAL +#ifdef HUGE +#define INFINITE_VALUE HUGE +#define NAN_VALUE HUGE +#endif +#else +#define INFINITE_VALUE HUGE_VAL +#define NAN_VALUE HUGE_VAL +#endif + +/* + * IEEE 754 Extended Precision + * + * Implementation here is the 80-bit extended precision + * format of Motorola 68881, Motorola 68882 and Motorola + * 68040 FPUs, as well as Intel 80x87 FPUs. + * + * See: + * http://www.freescale.com/files/32bit/doc/fact_sheet/BR509.pdf + */ +/* + * Exponent range: [-16383,16383] + * Precision for mantissa: 64 bits with no hidden bit + * Bias: 16383 + */ + +/* + * Write IEEE Extended Precision Numbers + */ +void +ieee754_write_extended(double in, unsigned char* out) +{ + int sgn, exp, shift; + double fraction, t; + unsigned int lexp, hexp; + unsigned long low, high; + + if (in == 0.0) { + memset(out, 0, 10); + return; + } + if (in < 0.0) { + in = fabs(in); + sgn = 1; + } else + sgn = 0; + + fraction = frexp(in, &exp); + + if (exp == 0 || exp > 16384) { + if (exp > 16384) /* infinite value */ + low = high = 0; + else { + low = 0x80000000; + high = 0; + } + exp = 32767; + goto done; + } + fraction = ldexp(fraction, 32); + t = floor(fraction); + low = (unsigned long) t; + fraction -= t; + t = floor(ldexp(fraction, 32)); + high = (unsigned long) t; + + /* Convert exponents < -16382 to -16382 (then they will be + * stored as -16383) */ + if (exp < -16382) { + shift = 0 - exp - 16382; + high >>= shift; + high |= (low << (32 - shift)); + low >>= shift; + exp = -16382; + } + exp += 16383 - 1; /* bias */ + +done: + lexp = ((unsigned int) exp) >> 8; + hexp = ((unsigned int) exp) & 0xFF; + + /* big endian */ + out[0] = ((unsigned char) sgn) << 7; + out[0] |= (unsigned char) lexp; + out[1] = (unsigned char) hexp; + out[2] = (unsigned char) (low >> 24); + out[3] = (unsigned char) ((low >> 16) & 0xFF); + out[4] = (unsigned char) ((low >> 8) & 0xFF); + out[5] = (unsigned char) (low & 0xFF); + out[6] = (unsigned char) (high >> 24); + out[7] = (unsigned char) ((high >> 16) & 0xFF); + out[8] = (unsigned char) ((high >> 8) & 0xFF); + out[9] = (unsigned char) (high & 0xFF); + + return; +} + + +/* + * Read IEEE Extended Precision Numbers + */ +double +ieee754_read_extended(unsigned char* in) +{ + int sgn, exp; + unsigned long low, high; + double out; + + /* Extract the components from the big endian buffer */ + sgn = (int) (in[0] >> 7); + exp = ((int) (in[0] & 0x7F) << 8) | ((int) in[1]); + low = (((unsigned long) in[2]) << 24) + | (((unsigned long) in[3]) << 16) + | (((unsigned long) in[4]) << 8) | (unsigned long) in[5]; + high = (((unsigned long) in[6]) << 24) + | (((unsigned long) in[7]) << 16) + | (((unsigned long) in[8]) << 8) | (unsigned long) in[9]; + + if (exp == 0 && low == 0 && high == 0) + return (sgn ? -0.0 : 0.0); + + switch (exp) { + case 32767: + if (low == 0 && high == 0) + return (sgn ? -INFINITE_VALUE : INFINITE_VALUE); + else + return (sgn ? -NAN_VALUE : NAN_VALUE); + default: + exp -= 16383; /* unbias exponent */ + + } + + out = ldexp((double) low, -31 + exp); + out += ldexp((double) high, -63 + exp); + + return (sgn ? -out : out); +} diff --git a/tools/aif2pcm/main.c b/tools/aif2pcm/main.c new file mode 100644 index 000000000..31842ff8e --- /dev/null +++ b/tools/aif2pcm/main.c @@ -0,0 +1,554 @@ +// Copyright(c) 2016 huderlem +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +/* extended.c */ +void ieee754_write_extended (double, unsigned char*); +double ieee754_read_extended (unsigned char*); + +#ifdef _MSC_VER + +#define FATAL_ERROR(format, ...) \ +do \ +{ \ + fprintf(stderr, format, __VA_ARGS__,); \ + exit(1); \ +} while (0) + +#else + +#define FATAL_ERROR(format, ...) \ +do \ +{ \ + fprintf(stderr, format, ##__VA_ARGS__); \ + exit(1); \ +} while (0) + +#endif // _MSC_VER + +typedef struct { + unsigned long num_samples; + char *samples; + unsigned char midi_note; + unsigned long loop_offset; + double sample_rate; +} AifData; + + +char * get_file_extension(char *filename) +{ + char *dot = strrchr(filename, '.'); + if (!dot || dot == filename) + { + FATAL_ERROR("Input file has no file extension.\n"); + } + + return dot + 1; +} + +void change_file_extension(char *filename, const char *new_extension) +{ + char *dot = strrchr(filename, '.'); + if (!dot || dot == filename) + { + FATAL_ERROR("Input file has no file extension.\n"); + } + + memcpy(dot + 1, new_extension, 3); +} + +AifData *read_aif(char * aif_file_data, unsigned long aif_file_data_size) +{ + AifData *aif_data = (AifData *)malloc(sizeof(AifData)); + + unsigned long pos = 0; + char chunk_name[5]; chunk_name[4] = '\0'; + char chunk_type[5]; chunk_type[4] = '\0'; + + // Check for FORM Chunk + memcpy(chunk_name, aif_file_data + pos, 4); + pos += 4; + if (strcmp(chunk_name, "FORM") != 0) + { + FATAL_ERROR("Input .aif file has invalid header Chunk '%s'!\n", chunk_name); + } + + // Read size of whole file. + unsigned long whole_chunk_size = aif_file_data[pos++] << 24; + whole_chunk_size |= (aif_file_data[pos++] << 16); + whole_chunk_size |= (aif_file_data[pos++] << 8); + whole_chunk_size |= (unsigned char)aif_file_data[pos++]; + + unsigned long expected_whole_chunk_size = aif_file_data_size - 8; + if (whole_chunk_size != expected_whole_chunk_size) + { + FATAL_ERROR("FORM Chunk ckSize '%lu' doesn't match actual size '%lu'!\n", whole_chunk_size, expected_whole_chunk_size); + } + + // Check for AIFF Form Type + memcpy(chunk_type, aif_file_data + pos, 4); + pos += 4; + if (strcmp(chunk_type, "AIFF") != 0) + { + FATAL_ERROR("FORM Type is '%s', but it must be AIFF!", chunk_type); + } + + unsigned long num_sample_frames = 0; + + // Read all the Chunks to populate the AifData struct. + while (pos < aif_file_data_size) + { + // Read Chunk id + memcpy(chunk_name, aif_file_data + pos, 4); + pos += 4; + + unsigned long chunk_size = (aif_file_data[pos++] << 24); + chunk_size |= (aif_file_data[pos++] << 16); + chunk_size |= (aif_file_data[pos++] << 8); + chunk_size |= aif_file_data[pos++]; + + if (strcmp(chunk_name, "COMM") == 0) + { + short num_channels = (aif_file_data[pos++] << 8); + num_channels |= (unsigned char)aif_file_data[pos++]; + if (num_channels != 1) + { + FATAL_ERROR("numChannels (%d) in the COMM Chunk must be 1!\n", num_channels); + } + + num_sample_frames = (aif_file_data[pos++] << 24); + num_sample_frames |= (aif_file_data[pos++] << 16); + num_sample_frames |= (aif_file_data[pos++] << 8); + num_sample_frames |= (unsigned char)aif_file_data[pos++]; + + short sample_size = (aif_file_data[pos++] << 8); + sample_size |= (unsigned char)aif_file_data[pos++]; + if (sample_size != 8) + { + FATAL_ERROR("sampleSize (%d) in the COMM Chunk must be 8!\n", sample_size); + } + + double sample_rate = ieee754_read_extended((unsigned char*)(aif_file_data + pos)); + pos += 10; + + aif_data->num_samples = num_sample_frames; + aif_data->sample_rate = sample_rate; + } + else if (strcmp(chunk_name, "MARK") == 0) + { + unsigned short num_markers = (aif_file_data[pos++] << 8); + num_markers |= (unsigned char)aif_file_data[pos++]; + + unsigned long loop_start = 0; + + // Read each marker and look for the "START" marker. + for (int i = 0; i < num_markers; i++) + { + unsigned short marker_id = (aif_file_data[pos++] << 8); + marker_id |= (unsigned char)aif_file_data[pos++]; + + unsigned long marker_position = (aif_file_data[pos++] << 24); + marker_position |= (aif_file_data[pos++] << 16); + marker_position |= (aif_file_data[pos++] << 8); + marker_position |= (unsigned char)aif_file_data[pos++]; + + // Marker id is a pascal-style string. + unsigned char marker_name_size = aif_file_data[pos++]; + char *marker_name = (char *)malloc((marker_name_size + 1) * sizeof(char)); + memcpy(marker_name, aif_file_data + pos, marker_name_size); + marker_name[marker_name_size] = '\0'; + pos += marker_name_size; + + if (strcmp(marker_name, "START") == 0) + { + loop_start = marker_position; + } + + free(marker_name); + } + + aif_data->loop_offset = loop_start; + } + else if (strcmp(chunk_name, "INST") == 0) + { + unsigned char midi_note = (unsigned char)aif_file_data[pos++]; + + aif_data->midi_note = midi_note; + + // Skip over data we don't need. + pos += 19; + } + else if (strcmp(chunk_name, "SSND") == 0) + { + // SKip offset and blockSize + pos += 8; + + char *sample_data = (char *)malloc(num_sample_frames * sizeof(char)); + memcpy(sample_data, aif_file_data + pos, num_sample_frames); + + aif_data->samples = sample_data; + pos += num_sample_frames; + } + else + { + // Skip over stuff we unsupported chunks. + pos += chunk_size; + } + } + + return aif_data; +} + +// Reads an .aif file and produces a .pcm file containing an array of 8-bit samples. +void aif2pcm(const char *aif_filename) +{ + // Get .pcm filename. + char pcm_filename[strlen(aif_filename)]; + strcpy(pcm_filename, aif_filename); + change_file_extension(pcm_filename, "pcm"); + + // Open the given .aif file so we can read its contents. + FILE *aif_file; + aif_file = fopen(aif_filename, "rb"); + if (!aif_file) + { + FATAL_ERROR("Failed to open '%s' for reading!\n", aif_filename); + } + + // Get file length. + fseek(aif_file, 0, SEEK_END); + unsigned long aif_file_length = ftell(aif_file); + fseek(aif_file, 0, SEEK_SET); + + // Create buffer for samples. + char *aif_file_data = (char *)malloc(aif_file_length * sizeof(char)); + if (!aif_file_data) + { + FATAL_ERROR("Failed to allocate buffer for aif file data!\n"); + } + + // Populate buffer from file. + unsigned long read = fread(aif_file_data, aif_file_length, 1, aif_file); + fclose(aif_file); + if (read <= 0) + { + FATAL_ERROR("Failed to read data from '%s'!\n", aif_filename); + } + + AifData *aif_data = read_aif(aif_file_data, aif_file_length); + + // Write the output .pcm file + FILE *pcm_file; + pcm_file = fopen(pcm_filename, "wb"); + fwrite(aif_data->samples, aif_data->num_samples, 1, pcm_file); + fclose(pcm_file); + + free(aif_data->samples); + free(aif_data); + free(aif_file_data); +} + +// Reads a .pcm file containing an array of 8-bit samples and produces an .aif file. +// See http://www-mmsp.ece.mcgill.ca/documents/audioformats/aiff/Docs/AIFF-1.3.pdf for .aif file specification. +void pcm2aif(const char *pcm_filename, char base_note, long pitch_adjust, long loop_start) +{ + // Get .aif filename. + char aif_filename[strlen(pcm_filename)]; + strcpy(aif_filename, pcm_filename); + change_file_extension(aif_filename, "aif"); + + // Open the given .pcm file so we can read its 8-bit samples. + FILE *pcm_file; + pcm_file = fopen(pcm_filename, "rb"); + if (!pcm_file) + { + FATAL_ERROR("Failed to open '%s' for reading!\n", pcm_filename); + } + + // Get file length. + fseek(pcm_file, 0, SEEK_END); + unsigned long num_samples = ftell(pcm_file); + fseek(pcm_file, 0, SEEK_SET); + + unsigned long num_samples_extended = num_samples * 1; + + // Create buffer for samples. + signed char *pcm_samples = (signed char *)malloc(num_samples_extended * sizeof(signed char)); + if (!pcm_samples) + { + FATAL_ERROR("Failed to allocate buffer for pcm samples!\n"); + } + + // Populate buffer from file. + unsigned long read = fread(pcm_samples, num_samples, 1, pcm_file); + fclose(pcm_file); + if (read <= 0) + { + FATAL_ERROR("Failed to read data from '%s'!\n", pcm_filename); + } + + // Allocate buffer for output .aif file. + unsigned long aif_file_size = 54 + 60 + num_samples_extended; + char *aif_buffer = (char *)malloc(aif_file_size * sizeof(char)); + if (!aif_buffer) + { + FATAL_ERROR("Failed to allocate buffer for aif file!\n"); + } + + long pos = 0; + + // First, write the FORM header chunk. + // FORM Chunk ckID + aif_buffer[pos++] = 'F'; + aif_buffer[pos++] = 'O'; + aif_buffer[pos++] = 'R'; + aif_buffer[pos++] = 'M'; + + // FORM Chunk ckSize + unsigned long data_size = aif_file_size - 8; + aif_buffer[pos++] = ((data_size >> 24) & 0xFF); + aif_buffer[pos++] = ((data_size >> 16) & 0xFF); + aif_buffer[pos++] = ((data_size >> 8) & 0xFF); + aif_buffer[pos++] = (data_size & 0xFF); + + // FORM Chunk formType + aif_buffer[pos++] = 'A'; + aif_buffer[pos++] = 'I'; + aif_buffer[pos++] = 'F'; + aif_buffer[pos++] = 'F'; + + // Next, write the Common Chunk + // Common Chunk ckID + aif_buffer[pos++] = 'C'; + aif_buffer[pos++] = 'O'; + aif_buffer[pos++] = 'M'; + aif_buffer[pos++] = 'M'; + + // Common Chunk ckSize + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 18; + + // Common Chunk numChannels + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 1; // 1 channel + + // Common Chunk numSampleFrames + aif_buffer[pos++] = ((num_samples_extended >> 24) & 0xFF); + aif_buffer[pos++] = ((num_samples_extended >> 16) & 0xFF); + aif_buffer[pos++] = ((num_samples_extended >> 8) & 0xFF); + aif_buffer[pos++] = (num_samples_extended & 0xFF); + + // Common Chunk sampleSize + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 8; // 8 bits per sample + + // Common Chunk sampleRate + double sample_rate = pitch_adjust / 1024.0; + unsigned char sample_rate_buffer[10]; + ieee754_write_extended(sample_rate, sample_rate_buffer); + for (int i = 0; i < 10; i++) + { + aif_buffer[pos++] = sample_rate_buffer[i]; + } + + // Marker Chunk ckID + aif_buffer[pos++] = 'M'; + aif_buffer[pos++] = 'A'; + aif_buffer[pos++] = 'R'; + aif_buffer[pos++] = 'K'; + + // Marker Chunk ckSize + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 24; + + // Marker Chunk numMarkers + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 2; + + // Marker loop start + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 1; // id = 1 + + aif_buffer[pos++] = ((loop_start >> 24) & 0xFF); + aif_buffer[pos++] = ((loop_start >> 16) & 0xFF); + aif_buffer[pos++] = ((loop_start >> 8) & 0xFF); + aif_buffer[pos++] = (loop_start & 0xFF); // position + + aif_buffer[pos++] = 5; // pascal-style string length + aif_buffer[pos++] = 'S'; + aif_buffer[pos++] = 'T'; + aif_buffer[pos++] = 'A'; + aif_buffer[pos++] = 'R'; + aif_buffer[pos++] = 'T'; // markerName + + // Marker loop end + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 2; // id = 2 + + long loop_end = num_samples; + aif_buffer[pos++] = ((loop_end >> 24) & 0xFF); + aif_buffer[pos++] = ((loop_end >> 16) & 0xFF); + aif_buffer[pos++] = ((loop_end >> 8) & 0xFF); + aif_buffer[pos++] = (loop_end & 0xFF); // position + + aif_buffer[pos++] = 3; // pascal-style string length + aif_buffer[pos++] = 'E'; + aif_buffer[pos++] = 'N'; + aif_buffer[pos++] = 'D'; + + + // Instrument Chunk ckID + aif_buffer[pos++] = 'I'; + aif_buffer[pos++] = 'N'; + aif_buffer[pos++] = 'S'; + aif_buffer[pos++] = 'T'; + + // Instrument Chunk ckSize + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 20; + + aif_buffer[pos++] = base_note; // baseNote + aif_buffer[pos++] = 0; // detune + aif_buffer[pos++] = 0; // lowNote + aif_buffer[pos++] = 127; // highNote + aif_buffer[pos++] = 1; // lowVelocity + aif_buffer[pos++] = 127; // highVelocity + aif_buffer[pos++] = 0; // gain (hi) + aif_buffer[pos++] = 0; // gain (lo) + + // Instrument Chunk sustainLoop + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 1; // playMode = ForwardLooping + + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 1; // beginLoop marker id + + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 2; // endLoop marker id + + // Instrument Chunk releaseLoop + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 1; // playMode = ForwardLooping + + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 1; // beginLoop marker id + + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 2; // endLoop marker id + + // Finally, write the Sound Data Chunk + // Sound Data Chunk ckID + aif_buffer[pos++] = 'S'; + aif_buffer[pos++] = 'S'; + aif_buffer[pos++] = 'N'; + aif_buffer[pos++] = 'D'; + + // Sound Data Chunk ckSize + unsigned long sound_data_size = num_samples_extended + 8; + aif_buffer[pos++] = ((sound_data_size >> 24) & 0xFF); + aif_buffer[pos++] = ((sound_data_size >> 16) & 0xFF); + aif_buffer[pos++] = ((sound_data_size >> 8) & 0xFF); + aif_buffer[pos++] = (sound_data_size & 0xFF); + + // Sound Data Chunk offset + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + + // Sound Data Chunk blockSize + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + aif_buffer[pos++] = 0; + + // Sound Data Chunk soundData + for (unsigned int i = 0; i < loop_start; i++) + { + aif_buffer[pos++] = pcm_samples[i]; + } + + int j = 0; + for (unsigned int i = loop_start; i < num_samples_extended; i++) + { + int pcm_index = loop_start + (j++ % (num_samples - loop_start - 1)); + aif_buffer[pos++] = pcm_samples[pcm_index]; + } + + // Write the .aif file contents. + FILE *aif_file; + aif_file = fopen(aif_filename, "wb"); + if (!aif_file) + { + FATAL_ERROR("Failed to open '%s' for writing!\n", aif_filename); + } + + fwrite(aif_buffer, 1, aif_file_size, aif_file); + fclose(aif_file); + + free(aif_buffer); + free(pcm_samples); + + printf("Wrote %s\n", aif_filename); +} + +int main(int argc, char **argv) +{ + if (argc < 2) + { + FATAL_ERROR("Usage: aif2pcm <aif_file>\n"); + } + + char *input_filename = argv[1]; + char *extension = get_file_extension(input_filename); + + if (strcmp(extension, "aif") == 0) + { + aif2pcm(input_filename); + } + else if (strcmp(extension, "pcm") == 0) + { + if (argc < 5) + { + FATAL_ERROR("Usage: aif2pcm <pcm_file> <midi_note> <pitch_adjust> <loop_start>\n"); + } + + char base_note = atoi(argv[2]); + long pitch_adjust = atol(argv[3]); + long loop_start = atol(argv[4]); + pcm2aif(input_filename, base_note, pitch_adjust, loop_start); + } + else + { + FATAL_ERROR("Input file must be .aif or .pcm: '%s'\n", input_filename); + } + + return 0; +} |