2

I'm using libogg and libogg, I've succeeded to add those libraries to my iPhone xCode project and encode my voice with Speex. The problem is that I cannot figure out how to pack those audio packet with ogg. Does someone know how a packet of that kind should look like or have a reference code I can use.

I know in Java it's pretty simple (you have a dedicated function for that) but not on iOS. Please help.

Idan
  • 9,880
  • 10
  • 47
  • 76

1 Answers1

5

UPD 10.09.2013: Please, see the demo project, which basically takes pcm audiodata from wave container, encodes it with speex codec and pack everything into ogg container. Maybe later I'll create a full-fledged library/framework for all that speex routines on IOS.

UPD 16.02.2015: The demo project is republished on GitHub.


I also have been experimenting with Speex on iOS recently, with varied success, but here is something I found. Basically, if you want to pack some speex-encoded voice into an ogg file, you need to do follow three steps (assuming libogg and libspeex are already compiled and added to the project).

1) Add the first ogg page with Speex header; libspeex provides built-in tools for that (the code below is from my project, not optimal, just for the sake of example):

// create speex header 
SpeexHeader spxHeader;
SpeexMode spxMode = speex_wb_mode;
int spxRate = 16000;
int spxNumberOfChannels = 1;
speex_init_header(&spxHeader, spxRate, spxNumberOfChannels, &spxMode);

// set audio and ogg packing parameters
spxHeader.vbr = 0;
spxHeader.bitrate = 16;
spxHeader.frame_size = 320;
spxHeader.frames_per_packet = 1;

// wrap speex header in ogg packet
int oggPacketSize;
_oggPacket.packet = (unsigned char *)speex_header_to_packet(&spxHeader, &oggPacketSize);
_oggPacket.bytes = oggPacketSize;
_oggPacket.b_o_s = 1;
_oggPacket.e_o_s = 0;
_oggPacket.granulepos = 0;
_oggPacket.packetno = 0;

// submit the packet to the ogg streaming layer
ogg_stream_packetin(&_oggStreamState, &_oggPacket);
free(_oggPacket.packet);

// form an ogg page
ogg_stream_flush(&_oggStreamState, &_oggPage);

// write the page to file
[_oggFile appendBytes:&_oggStreamState.header length:_oggStreamState.header_fill];
[_oggFile appendBytes:_oggStreamState.body_data length:_oggStreamState.body_fill];

2) Add the second ogg page with Vorbis comment:

// form any comment you like (I use custom struct with all fields)
vorbisCommentStruct *vorbisComment = calloc(sizeof(vorbisCommentStruct), sizeof(char));
...

// wrap Vorbis comment in ogg packet
_oggPacket.packet = (unsigned char *)vorbisComment;
_oggPacket.bytes = vorbisCommentLength;
_oggPacket.b_o_s = 0;
_oggPacket.e_o_s = 0;
_oggPacket.granulepos = 0;
_oggPacket.packetno = _oggStreamState.packetno;

// the rest should be same as in previous step
...

3) Add subsequent ogg pages with your speex-encoded audio in the similar manner.

First of all decide how many frames with audio data you want to have on every ogg page (0-255; I choose 79 quite arbitrarily):

_framesPerOggPage = 79;

Then for each frame:

// calculate current granule position of audio data within ogg file 
int curGranulePos = _spxSamplesPerFrame * _oggTotalFramesCount;

// wrap audio data in ogg packet
oggPacket.packet = (unsigned char *)spxFrame;
oggPacket.bytes = spxFrameLength;
oggPacket.granulepos = curGranulePos;
oggPacket.packetno = _oggStreamState.packetno;
oggPacket.b_o_s = 0;
oggPacket.e_o_s = 0;

// submit packets to streaming layer until their number reaches _framesPerOggPage
...

// if we've reached this limit, we're ready to create another ogg page

ogg_stream_flush(&_oggStreamState, &_oggPage);

[_oggFile appendBytes:&_oggStreamState.header length:_oggStreamState.header_fill];
[_oggFile appendBytes:_oggStreamState.body_data length:_oggStreamState.body_fill];

// finally, if this is the last frame, flush all remaining packets,
// which have been created but not packed into a page, to the last page 
// (don't forget to set oggPacket.e_o_s to 1 for this frame)

That's it. Hope it will help. Any corrections or questions are welcome.

mikejd
  • 1,540
  • 13
  • 18
  • Can you mention where and how you declare all those types? especially what is the _oggFile type? – Idan May 06 '13 at 18:09
  • @Idan, sorry for quite a late response, I had a lot of work lately. Regarding your question: the _oggFile is a NSMutableData object, though its name may sound confusing. I'll try to create a very basic sample project and post it here by the end of the week, if all turns out well. – mikejd May 07 '13 at 11:04
  • Thank you very much for creating this demo project but it doesn't compile. I think because all project is not relative. Just download that to another folder and see for yourself... – Idan May 11 '13 at 09:51
  • 1
    Oh, this probably happened because I renamed folder at the very last moment. Now the problem should be fixed and you can try to download & run the project once more. – mikejd May 12 '13 at 13:53
  • Got memory issues with this line: short *resampledAudioData = calloc(10 * 1024 * 1024, 2); It make the app crash, Why do you allocate memory that way? – Idan May 13 '13 at 20:59
  • Yes, this part is highly error-prone, and **absolutely** must be rewritten, as was mentioned in the project. But resampling was of secondary concern here, and given a very limited experience with memory management now, I just didn't come up with a better and stable solution. Closer look needed, or some help from other peers. – mikejd May 14 '13 at 10:26
  • Thanks for nice explanation, @mikejd. But I am unsure how to divide up frames for the 3rd page on, etc. The sample link is broken, but I'm hoping you can share that still. – s-t Feb 13 '15 at 20:09
  • 1
    @s-t, please find the demo project [here](https://github.com/mikejd/Speex-For-IOS). – mikejd Feb 15 '15 at 12:24
  • Thanks so much for posting the demo, @mikejd !!!, and I'm sorry about delay in thanks.. Just moved and finally got internet connected, computer dug up, etc. – s-t Feb 27 '15 at 07:11