Added WAVE writer and resample port, and also found out why audio quality is poor with DirectSound

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@358 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/include/pjmedia.h b/pjmedia/include/pjmedia.h
index 8cdc0b7..34264b6 100644
--- a/pjmedia/include/pjmedia.h
+++ b/pjmedia/include/pjmedia.h
@@ -34,6 +34,7 @@
 #include <pjmedia/jbuf.h>
 #include <pjmedia/null_port.h>
 #include <pjmedia/port.h>
+#include <pjmedia/resample.h>
 #include <pjmedia/rtcp.h>
 #include <pjmedia/rtp.h>
 #include <pjmedia/sdp.h>
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index c81693a..8e11104 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -68,5 +68,13 @@
 #endif
 
 
+/**
+ * Default file player/writer buffer size.
+ */
+#ifndef PJMEDIA_FILE_PORT_BUFSIZE
+#   define PJMEDIA_FILE_PORT_BUFSIZE    4000
+#endif
+
+
 #endif	/* __PJMEDIA_CONFIG_H__ */
 
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index 67c4d01..59e665d 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -440,6 +440,11 @@
  * Wave file too short.
  */
 #define PJMEDIA_EWAVETOOSHORT	    (PJMEDIA_ERRNO_START+182)    /* 220182 */
+/**
+ * @hideinitializer
+ * Sound frame is too large for file buffer.
+ */
+#define PJMEDIA_EFRMFILETOOBIG	    (PJMEDIA_ERRNO_START+183)    /* 220183 */
 
 
 /************************************************************
diff --git a/pjmedia/include/pjmedia/file_port.h b/pjmedia/include/pjmedia/file_port.h
index 75482c7..363f94e 100644
--- a/pjmedia/include/pjmedia/file_port.h
+++ b/pjmedia/include/pjmedia/file_port.h
@@ -26,11 +26,23 @@
 #include <pjmedia/port.h>
 
 
+
 PJ_BEGIN_DECL
 
 
 /**
- * Create file player port.
+ * Create a media port to play streams from a WAV file.
+ *
+ * @param pool		Pool to create memory buffers for this port.
+ * @param filename	File name to open.
+ * @param flags		Port creation flags.
+ * @param buf_size	Buffer size to be allocated. If the value is zero or
+ *			negative, the port will use default buffer size (which
+ *			is about 4KB).
+ * @param user_data	User data to be associated with the file player port.
+ * @param p_port	Pointer to receive the file port instance.
+ *
+ * @return		PJ_SUCCESS on success.
  */
 PJ_DECL(pj_status_t) pjmedia_file_player_port_create( pj_pool_t *pool,
 						      const char *filename,
@@ -41,6 +53,36 @@
 
 
 
+/**
+ * Create a media port to record streams to a WAV file. Note that the port
+ * must be closed properly (with #pjmedia_port_destroy()) so that the WAV
+ * header can be filled with correct values (such as the file length).
+ *
+ * @param pool		Pool to create memory buffers for this port.
+ * @param filename	File name.
+ * @param flags		Port creation flags.
+ * @param buf_size	Buffer size to be allocated. If the value is zero or
+ *			negative, the port will use default buffer size (which
+ *			is about 4KB).
+ * @param user_data	User data to be associated with the file writer port.
+ * @param p_port	Pointer to receive the file port instance.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_file_writer_port_create( pj_pool_t *pool,
+						      const char *filename,
+						      unsigned sampling_rate,
+						      unsigned channel_count,
+						      unsigned samples_per_frame,
+						      unsigned bits_per_sample,
+						      unsigned flags,
+						      pj_ssize_t buff_size,
+						      void *user_data,
+						      pjmedia_port **p_port );
+
+
+
+
 PJ_END_DECL
 
 
diff --git a/pjmedia/include/pjmedia/resample.h b/pjmedia/include/pjmedia/resample.h
index 63b3351..1def54c 100644
--- a/pjmedia/include/pjmedia/resample.h
+++ b/pjmedia/include/pjmedia/resample.h
@@ -25,10 +25,25 @@
  * @file reample.h
  * @brief Sample rate converter.
  */
-#include "pjmedia/types.h"
+#include <pjmedia/types.h>
+#include <pjmedia/port.h>
+
 
 PJ_BEGIN_DECL
 
+/*
+ * This file declares two types of API:
+ *
+ * Application can use #pjmedia_resample_create() and #pjmedia_resample_run()
+ * to convert a frame from source rate to destination rate. The inpuit frame 
+ * must have a constant length.
+ *
+ * Alternatively, application can create a resampling port with
+ * #pjmedia_resample_port_create() and connect the port to other ports to
+ * change the sampling rate of the samples.
+ */
+
+
 /**
  * Opaque resample session.
  */
@@ -59,7 +74,9 @@
 
 
 /**
- * Resample a frame.
+ * Use the resample session to resample a frame. The frame must have the
+ * same size and settings as the resample session, or otherwise the
+ * behavior is undefined.
  *
  * @param resample		The resample session.
  * @param input			Buffer containing the input samples.
@@ -70,6 +87,55 @@
 				    pj_int16_t *output );
 
 
+/**
+ * Get the input frame size of a resample session.
+ *
+ * @param resample		The resample session.
+ *
+ * @return			The frame size, in number of samples.
+ */
+PJ_DECL(unsigned) pjmedia_resample_get_input_size(pjmedia_resample *resample);
+
+
+/**
+ * Create a resample port. This creates a bidirectional resample session,
+ * which will resample frames when the port's get_frame() and put_frame()
+ * is called.
+ *
+ * When the resample port's get_frame() is called, this port will get
+ * a frame from the downstream port and resample the frame to the upstream
+ * port's clock rate before returning it to the caller.
+ *
+ * When the resample port's put_frame() is called, this port will resample
+ * the frame to the downstream's port clock rate before giving the frame
+ * to the downstream port.
+ *
+ * @param pool			Pool to allocate the structure and buffers.
+ * @param high_quality		If true, then high quality conversion will be
+ *				used, at the expense of more CPU and memory,
+ *				because temporary buffer needs to be created.
+ * @param large_filter		If true, large filter size will be used.
+ * @param downstream_rate	The sampling rate of the downstream port.
+ * @param upstream_rate		The sampling rate of the upstream port.
+ * @param channel_count		The number of channels. This argument is only
+ *				used for the port information. It does not
+ *				change the behavior of the resample port.
+ * @param samples_per_frame	Number of samples per frame from the downstream
+ *				port.
+ * @param p_port		Pointer to receive the resample port instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_resample_port_create( pj_pool_t *pool,
+						   pj_bool_t high_quality,
+						   pj_bool_t large_filter,
+						   unsigned downstream_rate,
+						   unsigned upstream_rate,
+						   unsigned channel_count,
+						   unsigned samples_per_frame,
+						   pjmedia_port **p_port );
+
+
 PJ_END_DECL
 
 #endif	/* __PJMEDIA_RESAMPLE_H__ */
diff --git a/pjmedia/include/pjmedia/wave.h b/pjmedia/include/pjmedia/wave.h
index cd4c3e6..201f976 100644
--- a/pjmedia/include/pjmedia/wave.h
+++ b/pjmedia/include/pjmedia/wave.h
@@ -32,6 +32,7 @@
 #define PJMEDIA_RIFF_TAG	('F'<<24|'F'<<16|'I'<<8|'R')
 #define PJMEDIA_WAVE_TAG	('E'<<24|'V'<<16|'A'<<8|'W')
 #define PJMEDIA_FMT_TAG		(' '<<24|'t'<<16|'m'<<8|'f')
+#define PJMEDIA_DATA_TAG	('a'<<24|'t'<<16|'a'<<8|'d')
 
 
 /**
@@ -69,6 +70,32 @@
 typedef struct pjmedia_wave_hdr pjmedia_wave_hdr;
 
 
+/**
+ * On big-endian hosts, this function swaps the byte order of the values
+ * in the WAVE header fields. On little-endian hosts, this function does 
+ * nothing.
+ *
+ * Application SHOULD call this function after reading the WAVE header
+ * chunks from a file.
+ *
+ * @param hdr	    The WAVE header.
+ */
+PJ_DECL(void) pjmedia_wave_hdr_file_to_host( pjmedia_wave_hdr *hdr );
+
+
+/**
+ * On big-endian hosts, this function swaps the byte order of the values
+ * in the WAVE header fields. On little-endian hosts, this function does 
+ * nothing.
+ *
+ * Application SHOULD call this function before writing the WAVE header
+ * to a file.
+ *
+ * @param hdr	    The WAVE header.
+ */
+PJ_DECL(void) pjmedia_wave_hdr_host_to_file( pjmedia_wave_hdr *hdr );
+
+
 PJ_END_DECL
 
 #endif	/* __PJMEDIA_WAVE_H__ */