2024-04-05 16:28:31 -04:00
# define _CRT_SECURE_NO_WARNINGS
# include <stdio.h>
# include <conio.h>
# include <stdint.h>
# include <portaudio.h>
# include <math.h>
# include <vector>
# include <fstream>
2024-10-20 16:18:51 -04:00
# include <queue>
2024-04-05 16:28:31 -04:00
# include "wavehead.h"
# include "opmplay.h"
2024-10-20 16:18:51 -04:00
# include "vgm.h"
# include "rss.h" // YM2608 internal ADPCM-A ROM dump
# include "ymfm/src/ymfm_opn.h"
2024-04-05 16:28:31 -04:00
# define WIN32_LEAN_AND_MEAN
# define WIN32_EXTRA_LEAN
# define NOMINMAX
# include <Windows.h>
enum {
CHANNELS = 2 ,
2024-04-06 05:35:20 -04:00
FRAMES_PER_BUFFER = 512 ,
2024-10-20 16:18:51 -04:00
MAX_FRAMES_PER_BUFFER = 4096 ,
2024-04-05 16:28:31 -04:00
} ;
2025-08-12 12:48:52 -04:00
//const double OPN_CLOCK_RATE = 3500000;
const double OPN_CLOCK_RATE = 3540000 ;
2024-10-20 16:18:51 -04:00
2024-04-05 16:28:31 -04:00
PaStreamParameters outputParameters ;
PaStream * stream ;
// console stuff
struct {
HANDLE hStdout , hScreenBuffer ;
COORD bufcoord , bufsize ;
SMALL_RECT bufDestRect ;
CHAR_INFO * buffer ; // the main buffer to write to
} console ;
struct opm_context_t {
// full context
opmplay_context_t opm ;
opmplay_io_t io ;
// delay count relative to sample rate
int32_t delay_count ;
2025-08-12 09:59:39 -04:00
int32_t delay_count_reload ;
2024-04-05 16:28:31 -04:00
} ;
2024-10-20 16:18:51 -04:00
struct vgm_context_t {
std : : vector < uint8_t > vgmfile ;
std : : vector < uint8_t > : : iterator vgmfile_it ;
VGMHeader * header ;
uint32_t loop_pos ;
uint32_t start , end ; // offsets
// delay count
int32_t delay_count ;
// rescaler for 44100hz delays
double rescaler ;
} ;
vgm_context_t vgmctx ;
2024-04-05 16:28:31 -04:00
opm_context_t opmctx ;
2024-10-20 16:18:51 -04:00
class opna_interface_t : public ymfm : : ymfm_interface {
public :
opna_interface_t ( ) { } ;
uint8_t ymfm_external_read ( ymfm : : access_class type , uint32_t address ) {
switch ( type ) {
case ymfm : : ACCESS_ADPCM_A : return YM2608_ADPCM_ROM [ address & 0x1FFF ] ;
default : return 0 ;
}
}
} ;
// register write queue
class opnx_register_queue_t {
private :
uint64_t write_delay ;
uint64_t clock ;
struct reg_entry_t {
uint64_t clock ;
2025-08-12 09:59:39 -04:00
int reg , data ;
2024-10-20 16:18:51 -04:00
} ;
std : : queue < reg_entry_t > reg_queue ;
2025-08-11 14:25:25 -04:00
ymfm : : ym2203 * chip ;
2024-10-20 16:18:51 -04:00
public :
opnx_register_queue_t ( ) : opnx_register_queue_t ( nullptr ) { }
2025-08-11 14:25:25 -04:00
opnx_register_queue_t ( ymfm : : ym2203 * _chip ) : clock ( 0 ) , write_delay ( 0 ) , chip ( _chip ) { }
void set_chip ( ymfm : : ym2203 * _chip ) { chip = _chip ; }
2024-10-20 16:18:51 -04:00
void reset ( ) {
clock = 0 ;
write_delay = 0 ;
while ( ! reg_queue . empty ( ) ) reg_queue . pop ( ) ; // flush queue
}
void add ( int chip , int reg , int data , uint64_t delay ) {
reg_entry_t r ;
r . clock = clock + write_delay ;
r . reg = reg ;
r . data = data ;
reg_queue . push ( r ) ;
write_delay + = delay ;
}
void pop_clock ( ) {
if ( ! reg_queue . empty ( ) & & ( reg_queue . front ( ) . clock < = clock ) ) {
auto & r = reg_queue . front ( ) ;
2025-08-11 14:25:25 -04:00
if ( chip ) {
2024-10-20 16:18:51 -04:00
chip - > write_address ( r . reg ) ;
chip - > write_data ( r . data ) ;
}
reg_queue . pop ( ) ;
}
clock + + ; write_delay = 0 ;
}
} ;
opna_interface_t opna_interface ;
2025-08-11 14:25:25 -04:00
ymfm : : ym2203 * opnachip [ 2 ] ;
opnx_register_queue_t opna_regqueue [ 2 ] ;
ymfm : : ym2203 : : output_data opna_out [ MAX_FRAMES_PER_BUFFER ] [ 2 ] ;
2024-04-05 16:28:31 -04:00
2025-08-12 09:59:39 -04:00
// hack af
uint8_t opn_reg_view [ 2 ] [ 256 ] ;
// generic output routine
void opn_write_reg ( int chip , int reg , int data ) {
if ( chip > = 2 ) return ;
opn_reg_view [ chip ] [ reg ] = data ;
opna_regqueue [ chip ] . add ( 0 , reg , data , 4 ) ;
}
2024-04-05 16:28:31 -04:00
// ------------------
// draw plain string
void drawstring ( const char * str , unsigned long x , unsigned long y , unsigned char attr ) {
CHAR_INFO * p = ( CHAR_INFO * ) console . buffer + ( console . bufsize . X * y ) + x ;
while ( * str ! = ' \0 ' ) {
p - > Char . AsciiChar = * str + + ;
p - > Attributes = attr ;
p + + ;
}
}
// draw string with attributes
// '\0' - end, '\xFF\xaa' - set attribute byte 'aa'
void drawastring ( const char * str , unsigned long x , unsigned long y ) {
CHAR_INFO * p = ( CHAR_INFO * ) console . buffer + ( console . bufsize . X * y ) + x ;
unsigned short attr = 0x07 ;
while ( * str ! = ' \0 ' ) if ( * str = = ' \xFF ' ) {
attr = ( * + + str ) ; str + + ;
}
else {
p - > Char . AsciiChar = * str + + ;
p - > Attributes = attr ;
p + + ;
}
}
// printf
int tprintf ( uint32_t x , uint32_t y , const char * format , . . . ) {
char buffer [ 1024 ] ; // large enough
va_list arglist ;
va_start ( arglist , format ) ;
int rtn = vsnprintf ( buffer , sizeof ( buffer ) , format , arglist ) ;
drawastring ( buffer , x , y ) ;
va_end ( arglist ) ;
return rtn ;
} ;
// -------------------
int console_open ( ) {
// Get a handle to the STDOUT screen buffer to copy from and
// create a new screen buffer to copy to.
console . hStdout = GetStdHandle ( STD_OUTPUT_HANDLE ) ;
console . hScreenBuffer = CreateConsoleScreenBuffer (
GENERIC_READ | // read/write access
GENERIC_WRITE ,
FILE_SHARE_READ |
FILE_SHARE_WRITE , // shared
NULL , // default security attributes
CONSOLE_TEXTMODE_BUFFER , // must be TEXTMODE
NULL ) ; // reserved; must be NULL
if ( console . hStdout = = INVALID_HANDLE_VALUE | |
console . hScreenBuffer = = INVALID_HANDLE_VALUE )
{
printf ( " CreateConsoleScreenBuffer failed - (%d) \n " , GetLastError ( ) ) ;
return 1 ;
}
// resize
2025-08-12 09:59:39 -04:00
console . bufsize . X = 132 ;
2024-04-05 16:28:31 -04:00
console . bufsize . Y = 40 ;
SetConsoleScreenBufferSize ( console . hScreenBuffer , console . bufsize ) ;
// allocate console buffer
console . buffer = new CHAR_INFO [ console . bufsize . X * console . bufsize . Y ] ;
memset ( console . buffer , 0 , sizeof ( CHAR_INFO ) * console . bufsize . X * console . bufsize . Y ) ;
// Make the new screen buffer the active screen buffer.
if ( ! SetConsoleActiveScreenBuffer ( console . hScreenBuffer ) )
{
printf ( " SetConsoleActiveScreenBuffer failed - (%d) \n " , GetLastError ( ) ) ;
return 1 ;
}
return 0 ;
}
void console_update ( ) {
console . bufDestRect . Top = 0 ;
console . bufDestRect . Left = 0 ;
console . bufDestRect . Bottom = console . bufsize . Y - 1 ;
console . bufDestRect . Right = console . bufsize . X - 1 ;
console . bufcoord . X = console . bufcoord . Y = 0 ;
WriteConsoleOutput (
console . hScreenBuffer , // screen buffer to write to
console . buffer , // buffer to copy from
console . bufsize , // col-row size of chiBuffer
console . bufcoord , // top left src cell in chiBuffer
& console . bufDestRect ) ; // dest. screen buffer rectangle
}
void console_done ( ) {
SetConsoleActiveScreenBuffer ( console . hStdout ) ;
}
// -------------------
2024-10-20 16:18:51 -04:00
// synth render
2024-04-05 16:28:31 -04:00
int synth_render ( int16_t * buffer , uint32_t num_samples ) {
int samples_to_render = num_samples ;
2025-08-11 14:25:25 -04:00
memset ( buffer , 0 , sizeof ( int16_t ) * 2 * num_samples ) ;
2024-10-20 16:18:51 -04:00
while ( samples_to_render > 0 ) {
2025-08-12 09:59:39 -04:00
if ( samples_to_render < opmctx . delay_count ) {
2024-10-20 16:18:51 -04:00
for ( int i = 0 ; i < samples_to_render ; i + + ) {
2025-08-11 14:25:25 -04:00
for ( int chip = 0 ; chip < 2 ; chip + + ) {
opna_regqueue [ chip ] . pop_clock ( ) ;
opnachip [ chip ] - > generate ( opna_out [ chip ] + i , 1 ) ;
opna_out [ chip ] [ i ] . clamp16 ( ) ;
2025-08-12 09:59:39 -04:00
buffer [ 2 * i + 0 ] + = 0.5 * ( 0.5 * opna_out [ chip ] [ i ] . data [ 0 ] + 0.5 * ( 1.0 * opna_out [ chip ] [ i ] . data [ 1 ] + 0.5 * opna_out [ chip ] [ i ] . data [ 2 ] + 0.0 * opna_out [ chip ] [ i ] . data [ 3 ] ) ) ; // mix FM and SSG
buffer [ 2 * i + 1 ] + = 0.5 * ( 0.5 * opna_out [ chip ] [ i ] . data [ 0 ] + 0.5 * ( 0.0 * opna_out [ chip ] [ i ] . data [ 1 ] + 0.5 * opna_out [ chip ] [ i ] . data [ 2 ] + 1.0 * opna_out [ chip ] [ i ] . data [ 3 ] ) ) ;
2025-08-11 14:25:25 -04:00
}
2024-10-20 16:18:51 -04:00
}
2025-08-12 09:59:39 -04:00
opmctx . delay_count - = samples_to_render ;
2025-08-11 14:25:25 -04:00
buffer + = 2 * samples_to_render ;
2024-10-20 16:18:51 -04:00
break ;
}
else {
// calculate new delay
2025-08-12 09:59:39 -04:00
for ( int i = 0 ; i < opmctx . delay_count ; i + + ) {
2025-08-11 14:25:25 -04:00
for ( int chip = 0 ; chip < 2 ; chip + + ) {
opna_regqueue [ chip ] . pop_clock ( ) ;
opnachip [ chip ] - > generate ( opna_out [ chip ] + i , 1 ) ;
opna_out [ chip ] [ i ] . clamp16 ( ) ;
2025-08-12 09:59:39 -04:00
buffer [ 2 * i + 0 ] + = 0.5 * ( 0.5 * opna_out [ chip ] [ i ] . data [ 0 ] + 0.5 * ( 1.0 * opna_out [ chip ] [ i ] . data [ 1 ] + 0.5 * opna_out [ chip ] [ i ] . data [ 2 ] + 0.0 * opna_out [ chip ] [ i ] . data [ 3 ] ) ) ; // mix FM and SSG
buffer [ 2 * i + 1 ] + = 0.5 * ( 0.5 * opna_out [ chip ] [ i ] . data [ 0 ] + 0.5 * ( 0.0 * opna_out [ chip ] [ i ] . data [ 1 ] + 0.5 * opna_out [ chip ] [ i ] . data [ 2 ] + 1.0 * opna_out [ chip ] [ i ] . data [ 3 ] ) ) ;
2025-08-11 14:25:25 -04:00
}
2024-10-20 16:18:51 -04:00
}
2025-08-12 09:59:39 -04:00
samples_to_render - = opmctx . delay_count ;
buffer + = 2 * opmctx . delay_count ;
2024-10-20 16:18:51 -04:00
2025-08-12 09:59:39 -04:00
opmplay_tick ( & opmctx . opm ) ;
opmctx . delay_count = opmctx . delay_count_reload ;
2024-10-20 16:18:51 -04:00
}
}
2024-04-05 16:28:31 -04:00
return 0 ;
}
2024-10-20 16:18:51 -04:00
int pa_init ( double sample_rate ) {
2024-04-05 16:28:31 -04:00
PaError err ;
// init portaudio
err = Pa_Initialize ( ) ;
if ( err ! = paNoError ) return 1 ;
outputParameters . device = Pa_GetDefaultOutputDevice ( ) ; /* default output device */
if ( outputParameters . device = = paNoDevice ) {
fprintf ( stderr , " Error: No default output device. \n " ) ;
return 1 ;
}
outputParameters . channelCount = CHANNELS ;
outputParameters . sampleFormat = paInt16 ;
2024-04-06 05:35:20 -04:00
outputParameters . suggestedLatency = 0.04 ;
2024-04-05 16:28:31 -04:00
outputParameters . hostApiSpecificStreamInfo = NULL ;
err = Pa_OpenStream (
& stream ,
NULL , /* no input */
& outputParameters ,
2024-10-20 16:18:51 -04:00
sample_rate ,
2024-04-05 16:28:31 -04:00
FRAMES_PER_BUFFER ,
0 , /* we won't output out of range samples so don't bother clipping them */
NULL , /* no callback, use blocking API */
NULL ) ; /* no callback, so no callback userData */
if ( err ! = paNoError ) return 1 ;
err = Pa_StartStream ( stream ) ;
if ( err ! = paNoError ) return 1 ;
return 0 ;
}
int pa_write ( void * data , int32_t count ) {
PaError err ;
err = Pa_WriteStream ( stream , data , count ) ;
return 0 ;
}
int pa_done ( ) {
PaError err ;
err = Pa_StopStream ( stream ) ;
if ( err ! = paNoError ) return 1 ;
// deinit portaudio
err = Pa_CloseStream ( stream ) ;
if ( err ! = paNoError ) return 1 ;
Pa_Terminate ( ) ;
return 0 ;
}
int main ( int argc , char * argv [ ] )
{
bool render_to_wave = ( argc > = 3 ) ;
2025-08-11 14:25:25 -04:00
uint32_t sample_rate ;
for ( int chip = 0 ; chip < 2 ; chip + + ) {
opnachip [ chip ] = new ymfm : : ym2203 ( opna_interface ) ;
if ( opnachip [ chip ] = = nullptr ) {
printf ( " error: unable to init ymfm! \n " ) ;
return 1 ;
}
opnachip [ chip ] - > reset ( ) ;
opnachip [ chip ] - > set_fidelity ( ymfm : : OPN_FIDELITY_MIN ) ;
sample_rate = opnachip [ chip ] - > sample_rate ( OPN_CLOCK_RATE ) ;
vgmctx . rescaler = ( ( double ) sample_rate / 44100.0 ) ;
printf ( " sample rate - %d hz \n " , sample_rate ) ;
opna_regqueue [ chip ] . reset ( ) ;
opna_regqueue [ chip ] . set_chip ( opnachip [ chip ] ) ;
2024-10-20 16:18:51 -04:00
}
if ( ! render_to_wave ) {
if ( pa_init ( sample_rate ) ! = 0 ) {
printf ( " error: unable to init PortAudio! \n " ) ;
return 1 ;
}
}
2024-04-05 16:28:31 -04:00
FILE * f = fopen ( argv [ 1 ] , " rb " ) ;
if ( f = = NULL ) {
printf ( " error: unable to open file! \n " ) ;
return 1 ;
}
2025-08-12 09:59:39 -04:00
# if 1
2024-04-05 16:28:31 -04:00
opmctx . io . type = OPMPLAY_IO_FILE ;
opmctx . io . io = f ;
int rtn ;
2025-08-12 09:59:39 -04:00
if ( ( rtn = opmplay_init ( & opmctx . opm ) ) ! = OPMPLAY_ERR_OK ) {
2024-04-05 16:28:31 -04:00
printf ( " unable to init OPMPlay (error = %d) \n " , rtn ) ;
return 1 ;
}
if ( ( rtn = opmplay_load_header ( & opmctx . opm , & opmctx . io ) ) ! = OPMPLAY_ERR_OK ) {
printf ( " unable to load OPM header (error = %d) \n " , rtn ) ;
return 1 ;
} ;
if ( ( rtn = opmplay_load_module ( & opmctx . opm , & opmctx . io ) ) ! = OPMPLAY_ERR_OK ) {
printf ( " unable to load OPM module (error = %d) \n " , rtn ) ;
return 1 ;
} ;
2025-08-12 09:59:39 -04:00
opmctx . delay_count_reload = opmctx . delay_count = ( ( double ) sample_rate / 50.0 ) ;
2024-10-20 16:18:51 -04:00
# else
// open VGM file, ready to parse
std : : ifstream infile ( argv [ 1 ] , std : : ios : : in | std : : ios : : binary ) ;
infile . unsetf ( std : : ios : : skipws ) ;
// get filesize
infile . seekg ( 0 , std : : ios : : end ) ;
uint64_t fsize = infile . tellg ( ) ;
infile . seekg ( 0 , std : : ios : : beg ) ;
// read whole file
vgmctx . vgmfile . reserve ( fsize ) ;
vgmctx . vgmfile . insert ( vgmctx . vgmfile . begin ( ) , std : : istream_iterator < uint8_t > ( infile ) , std : : istream_iterator < uint8_t > ( ) ) ;
// get header
vgmctx . header = reinterpret_cast < VGMHeader * > ( vgmctx . vgmfile . data ( ) ) ;
// check header
if ( memcmp ( vgmctx . header - > id , " Vgm \x20 " , sizeof ( vgmctx . header - > id ) ) ! = 0 ) {
printf ( " not a vaild VGM file! \n " ) ;
return 1 ;
}
// parse basic VGM structure
printf ( " VGM %d.%d file found \n " , ( vgmctx . header - > version > > 8 ) & 0xFF , vgmctx . header - > version & 0xFF ) ;
if ( vgmctx . header - > loopOffset ! = 0 ) vgmctx . loop_pos = vgmctx . header - > loopOffset + offsetof ( VGMHeader , loopOffset ) ;
vgmctx . end = vgmctx . header - > eofOffset + offsetof ( VGMHeader , eofOffset ) ;
vgmctx . start = ( ( vgmctx . header - > version < 0x150 ) ? 0x40 : vgmctx . header - > dataOffset + offsetof ( VGMHeader , dataOffset ) ) ;
vgmctx . vgmfile_it = vgmctx . vgmfile . begin ( ) + vgmctx . start ;
vgmctx . delay_count = 0 ;
# endif
2024-04-05 16:28:31 -04:00
2024-10-20 16:18:51 -04:00
console_open ( ) ;
2024-04-05 16:28:31 -04:00
std : : vector < int16_t > wavedata ;
int ff_pos = 0 , ff_counter = 0 ;
int16_t buf [ FRAMES_PER_BUFFER * CHANNELS ] = { 0 } ;
while ( 1 ) {
int rtn = synth_render ( buf , FRAMES_PER_BUFFER ) ;
if ( render_to_wave ) {
wavedata . insert ( wavedata . end ( ) , buf , buf + FRAMES_PER_BUFFER * CHANNELS ) ;
}
else {
pa_write ( buf , FRAMES_PER_BUFFER ) ;
}
2025-08-11 14:25:25 -04:00
ff_pos + = FRAMES_PER_BUFFER ;
2024-04-05 16:28:31 -04:00
// update console
memset ( console . buffer , 0 , sizeof ( CHAR_INFO ) * console . bufsize . X * console . bufsize . Y ) ;
2025-08-12 12:48:52 -04:00
tprintf ( 0 , 0 , " frame = %d " , opmctx . opm . pos . frame ) ;
2025-08-12 09:59:39 -04:00
{
int yy = 2 ;
for ( int ch = 0 ; ch < 6 ; ch + + ) {
int cc = ch / 3 ;
int co = ch % 3 ;
tprintf ( 0 , yy , " FM%d: [%02X %02X %02X %02X %02X %02X %02X] [%02X %02X %02X %02X %02X %02X %02X] [%02X %02X %02X %02X %02X %02X %02X] [%02X %02X %02X %02X %02X %02X %02X] - %02X %02X %02X " ,
ch ,
opn_reg_view [ cc ] [ 0x30 + co ] ,
opn_reg_view [ cc ] [ 0x40 + co ] ,
opn_reg_view [ cc ] [ 0x50 + co ] ,
opn_reg_view [ cc ] [ 0x60 + co ] ,
opn_reg_view [ cc ] [ 0x70 + co ] ,
opn_reg_view [ cc ] [ 0x80 + co ] ,
opn_reg_view [ cc ] [ 0x90 + co ] ,
opn_reg_view [ cc ] [ 0x34 + co ] ,
opn_reg_view [ cc ] [ 0x44 + co ] ,
opn_reg_view [ cc ] [ 0x54 + co ] ,
opn_reg_view [ cc ] [ 0x64 + co ] ,
opn_reg_view [ cc ] [ 0x74 + co ] ,
opn_reg_view [ cc ] [ 0x84 + co ] ,
opn_reg_view [ cc ] [ 0x94 + co ] ,
opn_reg_view [ cc ] [ 0x38 + co ] ,
opn_reg_view [ cc ] [ 0x48 + co ] ,
opn_reg_view [ cc ] [ 0x58 + co ] ,
opn_reg_view [ cc ] [ 0x68 + co ] ,
opn_reg_view [ cc ] [ 0x78 + co ] ,
opn_reg_view [ cc ] [ 0x88 + co ] ,
opn_reg_view [ cc ] [ 0x98 + co ] ,
opn_reg_view [ cc ] [ 0x3C + co ] ,
opn_reg_view [ cc ] [ 0x4C + co ] ,
opn_reg_view [ cc ] [ 0x5C + co ] ,
opn_reg_view [ cc ] [ 0x6C + co ] ,
opn_reg_view [ cc ] [ 0x7C + co ] ,
opn_reg_view [ cc ] [ 0x8C + co ] ,
opn_reg_view [ cc ] [ 0x9C + co ] ,
opn_reg_view [ cc ] [ 0xA0 + co ] ,
opn_reg_view [ cc ] [ 0xA4 + co ] ,
opn_reg_view [ cc ] [ 0xB0 + co ]
) ;
2025-08-12 12:48:52 -04:00
2025-08-12 09:59:39 -04:00
yy + + ;
2025-08-12 12:48:52 -04:00
if ( ( ch % 3 ) = = 2 ) {
tprintf ( 0 , yy , " EC%d: [%02X %02X %02X %02X %02X %02X %02X %02X] " ,
cc ,
opn_reg_view [ cc ] [ 0xAD ] ,
opn_reg_view [ cc ] [ 0xA9 ] ,
opn_reg_view [ cc ] [ 0xAC ] ,
opn_reg_view [ cc ] [ 0xA8 ] ,
opn_reg_view [ cc ] [ 0xAE ] ,
opn_reg_view [ cc ] [ 0xAA ] ,
opn_reg_view [ cc ] [ 0xA6 ] ,
opn_reg_view [ cc ] [ 0xA2 ]
) ;
yy + + ;
}
2025-08-12 09:59:39 -04:00
}
}
2025-08-11 14:25:25 -04:00
2024-04-05 16:28:31 -04:00
console_update ( ) ;
if ( _kbhit ( ) ) {
_getch ( ) ;
break ;
}
}
// write wave file
if ( render_to_wave ) {
// create headers
RIFF_Header riffHeader ;
memcpy ( & riffHeader . id , " RIFF " , sizeof ( riffHeader . id ) ) ;
memcpy ( & riffHeader . fourcc , " WAVE " , sizeof ( riffHeader . fourcc ) ) ;
riffHeader . size = sizeof ( riffHeader . fourcc ) + sizeof ( fmt_Header ) + sizeof ( chunk_Header ) + ( wavedata . size ( ) * sizeof ( decltype ( wavedata ) : : value_type ) ) ;
fmt_Header fmtHeader ;
memcpy ( & fmtHeader . id , " fmt " , sizeof ( fmtHeader . id ) ) ;
fmtHeader . size = sizeof ( fmtHeader ) - 8 ;
fmtHeader . wFormatTag = 1 ; // plain uncompressed PCM
2024-10-20 16:18:51 -04:00
fmtHeader . nSamplesPerSec = sample_rate ;
2024-04-05 16:28:31 -04:00
fmtHeader . nBlockAlign = CHANNELS ;
2024-10-20 16:18:51 -04:00
fmtHeader . nAvgBytesPerSec = sample_rate * CHANNELS ;
2024-04-05 16:28:31 -04:00
fmtHeader . nChannels = CHANNELS ;
fmtHeader . wBitsPerSample = 8 ;
chunk_Header dataHeader ;
memcpy ( & dataHeader . id , " data " , sizeof ( dataHeader . id ) ) ;
dataHeader . size = ( wavedata . size ( ) * sizeof ( decltype ( wavedata ) : : value_type ) ) ;
// write wave file
FILE * outfile = fopen ( " out.wav " , " wb " ) ;
fwrite ( & riffHeader , sizeof ( riffHeader ) , 1 , outfile ) ;
fwrite ( & fmtHeader , sizeof ( fmtHeader ) , 1 , outfile ) ;
fwrite ( & dataHeader , sizeof ( dataHeader ) , 1 , outfile ) ;
fwrite ( wavedata . data ( ) , ( wavedata . size ( ) * sizeof ( decltype ( wavedata ) : : value_type ) ) , 1 , outfile ) ;
fclose ( outfile ) ;
} else
pa_done ( ) ;
console_done ( ) ;
return 0 ;
}