DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/aviwriter/riff_wav_writer.cpp
00001 /*
00002  *  Copyright (C) 2018-2020 Jon Campbell
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 /* Shut up! */
00020 #define _CRT_NONSTDC_NO_DEPRECATE
00021 
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <assert.h>
00027 #include <unistd.h>
00028 #include <fcntl.h>
00029 #include <stdio.h>
00030 #include <math.h>
00031 #ifdef _MSC_VER
00032 # include <io.h>
00033 #endif
00034 
00035 #ifndef O_BINARY
00036 # define O_BINARY 0
00037 #endif
00038 
00039 /* FIXME: I made the mistake of putting critical calls in assert() calls, which under MSVC++ may evaluate to nothing in Release builds */
00040 #if defined(_MSC_VER) || defined (__MINGW32__)
00041 # ifdef NDEBUG
00042 #  undef assert
00043 #  define assert(x) x
00044 # endif
00045 #endif
00046 
00047 #include "riff_wav_writer.h"
00048 #include "rawint.h"
00049 
00050 riff_wav_writer *riff_wav_writer_create() {
00051         riff_wav_writer *w = (riff_wav_writer*)malloc(sizeof(riff_wav_writer));
00052         if (w == NULL) return NULL;
00053         memset(w,0,sizeof(*w));
00054 
00055         if ((w->riff = riff_stack_create(32)) == NULL)
00056                 return riff_wav_writer_destroy(w);
00057 
00058         w->fd = -1;
00059         w->own_fd = 0;
00060         w->state = RIFF_WRITER_INIT;
00061         return w;
00062 }
00063 
00064 /* NOTE: for WAVEFORMATEX use set_format_ex */
00065 int riff_wav_writer_set_format(riff_wav_writer *w,windows_WAVEFORMAT *f) {
00066         if (w == NULL || f == NULL)
00067                 return 0;
00068         if (w->state != RIFF_WRITER_INIT)
00069                 return 0;
00070         if (w->fmt != NULL)
00071                 return 0;
00072 
00073         w->fmt_len = sizeof(*f);
00074         if ((w->fmt = malloc(w->fmt_len)) == NULL)
00075                 return 0;
00076 
00077         memcpy(w->fmt,f,w->fmt_len);
00078         return 1;
00079 }
00080 
00081 int riff_wav_writer_set_format_old(riff_wav_writer *w,windows_WAVEFORMATOLD *f) {
00082         if (w == NULL || f == NULL)
00083                 return 0;
00084         if (w->state != RIFF_WRITER_INIT)
00085                 return 0;
00086         if (w->fmt != NULL)
00087                 return 0;
00088 
00089         w->fmt_len = sizeof(*f);
00090         if ((w->fmt = malloc(w->fmt_len)) == NULL)
00091                 return 0;
00092 
00093         memcpy(w->fmt,f,w->fmt_len);
00094         return 1;
00095 }
00096 
00097 int riff_wav_writer_set_format_ex(riff_wav_writer *w,windows_WAVEFORMATEX *f,size_t len) {
00098         if (w == NULL || f == NULL)
00099                 return 0;
00100         if (w->state != RIFF_WRITER_INIT)
00101                 return 0;
00102         if (w->fmt != NULL)
00103                 return 0;
00104 
00105         w->fmt_len = sizeof(windows_WAVEFORMAT);
00106         if (__le_u16(&f->cbSize) != 0u) w->fmt_len += (size_t)2u + __le_u16(&f->cbSize);
00107         if (w->fmt_len > len)
00108                 return 0;
00109         if ((w->fmt = malloc(w->fmt_len)) == NULL)
00110                 return 0;
00111 
00112         memcpy(w->fmt,f,w->fmt_len);
00113         return 1;
00114 }
00115 
00116 int riff_wav_writer_assign_file(riff_wav_writer *w,int fd) {
00117         if (w->state != RIFF_WRITER_INIT)
00118                 return 0;
00119         if (w->fd >= 0 && w->own_fd)
00120                 return 0;
00121 
00122         w->fd = fd;
00123         w->own_fd = 0;
00124         if (!riff_stack_assign_fd(w->riff,fd))
00125                 return 0;
00126 
00127         assert(riff_stack_empty(w->riff));
00128         assert(riff_stack_prepare_for_writing(w->riff,1));
00129         return 1;
00130 }
00131 
00132 int riff_wav_writer_open_file(riff_wav_writer *w,const char *path) {
00133         if (w->state != RIFF_WRITER_INIT)
00134                 return 0;
00135         if (w->fd >= 0)
00136                 return 0;
00137         if ((w->fd = open(path,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,0644)) < 0)
00138                 return 0;
00139         if (!riff_stack_assign_fd(w->riff,w->fd)) {
00140                 close(w->fd);
00141                 w->fd = -1;
00142         }
00143         w->own_fd = 1;
00144         assert(riff_stack_empty(w->riff));
00145         assert(riff_stack_prepare_for_writing(w->riff,1));
00146         return 1;
00147 }
00148 
00149 void riff_wav_writer_fsync(riff_wav_writer *w) {
00150         if (w->fd >= 0 && w->riff)
00151                 riff_stack_header_sync_all(w->riff);
00152 }
00153 
00154 int riff_wav_writer_begin_header(riff_wav_writer *w) {
00155         riff_chunk chunk;
00156 
00157         if (w->state != RIFF_WRITER_INIT)
00158                 return 0;
00159         if (w->fmt == NULL)
00160                 return 0;
00161 
00162         /* RIFF:WAVE */
00163         assert(riff_stack_begin_new_chunk_here(w->riff,&chunk));
00164         assert(riff_stack_set_chunk_list_type(&chunk,riff_RIFF,riff_fourcc_const('W','A','V','E')));
00165         assert(riff_stack_push(w->riff,&chunk)); /* NTS: we can reuse chunk, the stack copies it here */
00166 
00167         /* 'fmt ' */
00168         assert(riff_stack_begin_new_chunk_here(w->riff,&chunk));
00169         assert(riff_stack_set_chunk_data_type(&chunk,riff_fourcc_const('f','m','t',' ')));
00170         assert(riff_stack_push(w->riff,&chunk)); /* NTS: we can reuse chunk, the stack copies it here */
00171         assert((int)riff_stack_write(w->riff,riff_stack_top(w->riff),w->fmt,w->fmt_len) == (int)w->fmt_len);
00172         riff_stack_pop(w->riff);
00173 
00174         /* state change */
00175         w->state = RIFF_WRITER_HEADER;
00176         return 1;
00177 }
00178 
00179 int riff_wav_writer_begin_data(riff_wav_writer *w) {
00180         riff_chunk chunk;
00181 
00182         if (w->state != RIFF_WRITER_HEADER)
00183                 return 0;
00184 
00185         /* 'data' */
00186         assert(riff_stack_begin_new_chunk_here(w->riff,&chunk));
00187         assert(riff_stack_set_chunk_data_type(&chunk,riff_fourcc_const('d','a','t','a')));
00188         assert(riff_stack_push(w->riff,&chunk)); /* NTS: we can reuse chunk, the stack copies it here */
00189         riff_stack_header_sync_all(w->riff);
00190 
00191         /* state change */
00192         w->state = RIFF_WRITER_DATA;
00193         return 1;
00194 }
00195 
00196 riff_wav_writer *riff_wav_writer_destroy(riff_wav_writer *w) {
00197         if (w) {
00198                 riff_wav_writer_fsync(w);
00199                 if (w->fmt) free(w->fmt);
00200                 if (w->fd >= 0 && w->own_fd) close(w->fd);
00201                 if (w->riff) riff_stack_destroy(w->riff);
00202                 free(w);
00203         }
00204         return NULL;
00205 }
00206 
00207 int riff_wav_writer_data_write(riff_wav_writer *w,void *buffer,size_t len) {
00208         riff_chunk *c;
00209 
00210         if (w == NULL)
00211                 return 0;
00212         if (w->state != RIFF_WRITER_DATA)
00213                 return 0;
00214         if ((c=riff_stack_top(w->riff)) == NULL)
00215                 return 0;
00216         if (c->fourcc != riff_fourcc_const('d','a','t','a'))
00217                 return 0;
00218 
00219         return (int)riff_stack_write(w->riff,riff_stack_top(w->riff),buffer,len);
00220 }
00221 
00222 int64_t riff_wav_writer_data_seek(riff_wav_writer *w,int64_t offset) {
00223         riff_chunk *c;
00224 
00225         if (w == NULL)
00226                 return 0;
00227         if (w->state != RIFF_WRITER_DATA)
00228                 return 0;
00229         if ((c=riff_stack_top(w->riff)) == NULL)
00230                 return 0;
00231         if (c->fourcc != riff_fourcc_const('d','a','t','a'))
00232                 return 0;
00233 
00234         return riff_stack_seek(w->riff,riff_stack_top(w->riff),offset);
00235 }
00236 
00237 int64_t riff_wav_writer_data_tell(riff_wav_writer *w) {
00238         riff_chunk *c;
00239 
00240         if (w == NULL)
00241                 return 0;
00242         if (w->state != RIFF_WRITER_DATA)
00243                 return 0;
00244         if ((c=riff_stack_top(w->riff)) == NULL)
00245                 return 0;
00246         if (c->fourcc != riff_fourcc_const('d','a','t','a'))
00247                 return 0;
00248 
00249         return c->write_offset;
00250 }
00251 
00252 int riff_wav_writer_end_data(riff_wav_writer *w) {
00253         riff_chunk *c;
00254 
00255         if (w == NULL)
00256                 return 0;
00257         if (w->state != RIFF_WRITER_DATA)
00258                 return 0;
00259         if ((c=riff_stack_top(w->riff)) == NULL)
00260                 return 0;
00261         if (c->fourcc != riff_fourcc_const('d','a','t','a'))
00262                 return 0;
00263 
00264         /* state change */
00265         riff_stack_pop(w->riff);
00266         w->state = RIFF_WRITER_FOOTER;
00267         return 1;
00268 }
00269