【问题标题】:Converting from C: fputc and fwrite in C#?从 C 转换:C# 中的 fputc 和 fwrite?
【发布时间】:2011-09-27 16:06:44
【问题描述】:

问题:为了给libespeak写一个C#接口,我需要将回调SynthCallback转换为C#

请参阅下面的C 代码。
您可能需要此作为参考:
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
http://www-mmsp.ece.mcgill.ca/documents/audioformats/wave/wave.html

基本上,

espeak_initialize
espeak_SetSynthCallback(SynthCallback);
espeak_SetParameter(espeakRATE, 510, 0);        
espeak_Synth(".", 20, 0, POS_CHARACTER, 0, 0, NULL, NULL);

是 DllImport-ed 函数。 而且我已经让它们异步工作,没有文件。

现在我想让同步版本处理文件,但我有一个小问题:

回调函数

static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)

首先,我需要创建一个委托,我可以将此函数传递给C dll/so。 这不是问题,但如果我制作short *wav to a System.IntPtr,我如何将数据写入文件?

换句话说:有人可以帮我把fwritefputcWrite4Bytes 转换成正确的C#吗?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <espeak/speak_lib.h>

// gcc -o mine speak.cpp -I/usr/include/espeak/ -lespeak


FILE *f_wavfile = NULL;

static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events);


// Write 4 bytes to a file, least significant first
static void Write4Bytes(FILE *f, int value)
{
    int ix;

    for(ix=0; ix<4; ix++)
    {
        fputc(value & 0xff,f);
        value = value >> 8;
    }
}



int OpenWavFile(char *path, int rate)
{
    static unsigned char wave_hdr[44] = {
        'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ',
        0x10,0,0,0,1,0,1,0,  9,0x3d,0,0,0x12,0x7a,0,0,
        2,0,0x10,0,'d','a','t','a',  0x00,0xf0,0xff,0x7f
    };


    if(path == NULL)
        return(2);

    if(path[0] == 0)
        return(0);

    if(strcmp(path,"stdout")==0)
        f_wavfile = stdout;
    else
        f_wavfile = fopen(path,"wb");

    if(f_wavfile == NULL)
    {
        fprintf(stderr,"Can't write to: '%s'\n",path);
        return(1);
    }


    fwrite(wave_hdr, 1, 24, f_wavfile);
    Write4Bytes(f_wavfile, rate);
    Write4Bytes(f_wavfile, rate * 2);
    fwrite(&wave_hdr[32], 1, 12, f_wavfile);
    return(0);
}   //  end of OpenWavFile



static void CloseWavFile()
{
    unsigned int pos;

    if((f_wavfile==NULL) || (f_wavfile == stdout))
        return;

    fflush(f_wavfile);
    pos = ftell(f_wavfile);

    fseek(f_wavfile, 4, SEEK_SET);
    Write4Bytes(f_wavfile, pos - 8);

    fseek(f_wavfile, 40, SEEK_SET);
    Write4Bytes(f_wavfile, pos - 44);

    fclose(f_wavfile);
} // end of CloseWavFile


int main() 
{   
    char buf[22050];
    int i = 0;
    memset(&buf, 0, sizeof(buf));
    // OpenWavFile((char*) "test.wav", 22050);
    int SampleRate = espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 300, NULL, 0);

    OpenWavFile((char*) "test.wav", SampleRate);

    espeak_SetSynthCallback(SynthCallback);
    //espeak_SetParameter(espeakRATE, 510, 0);
    //espeak_SetParameter(espeakRANGE, 75, 0);
    for (i=0; i < 9;i++) 
    {
        /*
        espeak_ERROR espeak_Synth(
            const void *text,
            size_t size,
            unsigned int position,
            espeak_POSITION_TYPE position_type,
            unsigned int end_position,
            unsigned int flags,
            unsigned int* unique_identifier,
            void* user_data);
        */
        //espeak_POSITION_TYPE.POS_CHARACTER
        espeak_Synth("test", 10, 0, POS_CHARACTER, 0, 0, NULL, NULL);
        fwrite(buf, 1, 5512, f_wavfile);
        espeak_Synth(".", 20, 0, POS_CHARACTER, 0, 0, NULL, NULL);
        fwrite(buf, 1, 22050, f_wavfile);
    }
    CloseWavFile();
}


static int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
{
    if (wav == NULL) 
        return 0;

    if (numsamples > 0) 
    {
        fwrite(wav, numsamples * 2, 1, f_wavfile);
    }

    return 0;
}

【问题讨论】:

    标签: c# c++ c text-to-speech espeak


    【解决方案1】:
    FileStream sink;
    Int32 SynthCallback(IntPtr wav, Int32 numsamples, IntPtr events)
    {
        var wavm = new Int16[numsamples];
        Marshal.Copy(wav, wavm, 0, numsamples);
        // and do something with wavm
    
        //or
        var wavbytes = new Byte[numsamples * 2];
        Marshal.Copy(wav, wavbytes, 0, numsamples*2);
        sink.Write(wavbytes, 0, numsamples*2);
    }
    

    对于编写 32 整数,您可以使用 BitConverter.GetBytesFileStream.Write 或者 BinaryWriter

    【讨论】:

    • 我很惊讶,第一次尝试就成功了,使用 BinaryWriter + BitConverter.GetBytes。
    【解决方案2】:

    您可以 P/Invoke CreateFile/WriteFile 直接写入 IntPtr。

    【讨论】:

    • 这需要导出函数,但事实并非如此,而且从托管调用到非托管,调用到托管回调,然后从那里返回到非托管代码是没有意义的......
    • @Quandary:那么你需要将 IntPtr 中的数据复制到一个 short[] 中。
    猜你喜欢
    • 1970-01-01
    • 2012-12-10
    • 2016-12-29
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多