Feedback

I’d love to hear feedback from users of the site. I know some of you are put off by having to register, but it is the only way I know of to avoid comment spamming. So please, if you have any thoughts, criticisms or requests, sign up and let me know. I’ll write up a proper privacy policy soon, but in the meantime here’s the quick version: I won’t spam you and I will not sell or give your email address to anyone.

Cheers,
idz

5 Responses to Feedback

  1. andreu.santaren says:

    Hello iDZ!

    I would like to thank you for you wonderful tutorials, specially for the ogg-vorbis compiling series, it really saved my live! I want to share with you a class that I created for iOS to record audio in PCM and encode to Ogg-Vorbis. It’s completely standalone – just need to add the ogg and vorbis frameworks ;)

    To use this class call the selectors “start” and “stop”, to start-stop the process. The result is a pair of files audiorec.pcm & audiorec.ogg that you can get by iTunes.

    Thank you!

    AudioRec.h:

    #import
    #import
    #import
    #import
    #import
    #import

    #define NUM_BUFFERS 3
    #define AUDIORECPCM @”audiorec.pcm”
    #define AUDIORECOGG @”audiorec.ogg”

    // Struct defining recording state
    typedef struct
    {
    AudioStreamBasicDescription dataFormat;
    AudioQueueRef queue;
    AudioQueueBufferRef buffers[NUM_BUFFERS];
    AudioFileID audioFile;
    SInt64 currentPacket;
    bool recording;
    } RecordState;

    @interface AudioRec : NSObject
    {
    RecordState recordState;
    }

    – (void)startRecording;
    – (void)stopRecording;

    @end

    AudioRec.m:

    #import “AudioRec.h”

    void AudioInputCallback(void * inUserData, // Custom audio metadata
    AudioQueueRef inAQ,
    AudioQueueBufferRef inBuffer,
    const AudioTimeStamp *inStartTime,
    UInt32 inNumberPacketDescriptions,
    const AudioStreamPacketDescription *inPacketDescs);

    id refToSelf;

    // Ogg-Vorbis definitions
    ogg_stream_state os; /* take physical pages, weld into a logical
    stream of packets */
    ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */
    ogg_packet op; /* one raw packet of data for decode */

    vorbis_info vi; /* struct that stores all the static vorbis bitstream
    settings */
    vorbis_comment vc; /* struct that stores all the user comments */

    vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
    vorbis_block vb; /* local working space for packet->PCM decode */

    int eos, ret;

    @implementation AudioRec

    – init
    {
    if (self = [super init])
    {
    // Your initialization code here
    refToSelf = self;
    }
    return self;
    }

    // New sample buffer generated
    void AudioInputCallback(void * inUserData,
    AudioQueueRef inAQ,
    AudioQueueBufferRef inBuffer,
    const AudioTimeStamp * inStartTime,
    UInt32 inNumberPacketDescriptions,
    const AudioStreamPacketDescription * inPacketDescs)
    {
    RecordState * recordState = (RecordState*)inUserData;
    if (!recordState->recording)
    {
    NSLog(@”Not recording, returning”);
    }

    NSLog(@”Total current bytes = %lld”, recordState->currentPacket * 2);
    NSLog(@”Generated PCM sample buffer size = %ld”, inBuffer->mAudioDataByteSize);

    // In inBuffer->mAudioData we have a buffer of size inBuffer->mAudioDataByteSize with recorded samples
    size_t wrBytes = [refToSelf appendToFile:AUDIORECPCM withBuffer:inBuffer->mAudioData withSize:inBuffer->mAudioDataByteSize];

    // Encode to Ogg-Vorbis (yeeeeeha!!)
    doOggVorbisEncoding(inBuffer->mAudioData, inBuffer->mAudioDataByteSize);

    if (wrBytes != 0)
    {
    recordState->currentPacket += inNumberPacketDescriptions;
    }

    // Reuse buffer for more samples
    AudioQueueEnqueueBuffer(recordState->queue, inBuffer, 0, NULL);
    }

    – (void)startRecording
    {
    // Ogg-Vorbis, End Of Stream (eos) flag
    eos = 0;

    // Create files or truncate it to zero
    [self createFile:AUDIORECPCM];
    [self createFile:AUDIORECOGG];

    setupOggVorbisEncoder();

    [self setupAudioFormat:&recordState.dataFormat];

    recordState.currentPacket = 0;

    OSStatus status;
    status = AudioQueueNewInput(&recordState.dataFormat,
    AudioInputCallback,
    &recordState,
    CFRunLoopGetCurrent(),
    kCFRunLoopCommonModes,
    0,
    &recordState.queue);

    if (status == 0)
    {
    // Prime recording buffers with empty data
    for (int i = 0; i < NUM_BUFFERS; i++)
    {
    AudioQueueAllocateBuffer(recordState.queue, 16000, &recordState.buffers[i]);
    AudioQueueEnqueueBuffer (recordState.queue, recordState.buffers[i], 0, NULL);
    }

    recordState.recording = true;
    status = AudioQueueStart(recordState.queue, NULL);
    }

    if (status != 0)
    {
    [self stopRecording];
    NSLog(@"Record Failed");
    }
    }

    – (void)stopRecording
    {
    recordState.recording = false;

    AudioQueueStop(recordState.queue, true);

    for(int i = 0; i mSampleRate = 8000.0;
    format->mFormatID = kAudioFormatLinearPCM;
    format->mFramesPerPacket = 1;
    format->mChannelsPerFrame = 1;
    format->mBytesPerFrame = 2;
    format->mBytesPerPacket = 2;
    format->mBitsPerChannel = 16;
    format->mReserved = 0;
    format->mFormatFlags =
    kLinearPCMFormatFlagIsBigEndian |
    kLinearPCMFormatFlagIsSignedInteger |
    kLinearPCMFormatFlagIsPacked;
    }

    – (void)createFile:(NSString *)file
    {
    NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *filePath = [documentsDirectoryPath stringByAppendingPathComponent:file];

    FILE *fd = fopen([filePath cStringUsingEncoding:NSUTF8StringEncoding], “w+b”);

    if (fd == NULL)
    {
    // error
    NSLog(@”Error opening audio file”);
    fclose(fd);

    return;
    }

    fclose(fd);
    }

    – (FILE *)openFileToAppend:(NSString *)file
    {
    NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString *filePath = [documentsDirectoryPath stringByAppendingPathComponent:file];

    FILE *fd = fopen([filePath cStringUsingEncoding:NSUTF8StringEncoding], “r+b”);

    if (fd == NULL)
    {
    // error
    NSLog(@”Error opening audio file”);
    fclose(fd);

    return NULL;
    }

    fseek(fd, 0L, SEEK_END);

    return fd;
    }

    – (size_t)appendToFile:(NSString *)file withBuffer:(void *)buffer withSize:(size_t)size
    {
    FILE *fp = [refToSelf openFileToAppend:file];
    size_t wrBytes = fwrite(buffer, 1, size, fp);

    if (wrBytes == 0)
    {
    NSLog(@”appendToFile: Zero bytes written”);
    }

    fclose(fp);

    return wrBytes;
    }

    #pragma mark – Ogg-Vorbis Encoder

    bool setupOggVorbisEncoder()
    {
    NSLog(@”setupOggVorbisEncoder”);

    vorbis_info_init(&vi);
    ret = vorbis_encode_init_vbr(&vi, 1, 8000, 0.4); // 1 channel, 8KHz

    if (ret)
    {
    NSLog(@”setupOggVorbisEncoder Failed Configuring Encoder!”);
    return false;
    }

    vorbis_comment_init(&vc);
    vorbis_comment_add_tag(&vc, “ENCODER”, “El Fari”);

    /* set up the analysis state and auxiliary encoding storage */
    vorbis_analysis_init(&vd, &vi);
    vorbis_block_init(&vd, &vb);

    /* set up our packet->stream encoder */
    /* pick a random serial number; that way we can more likely build
    chained streams just by concatenation */
    srand(time(NULL));
    ogg_stream_init(&os, rand());

    /* Vorbis streams begin with three headers; the initial header (with
    most of the codec setup parameters) which is mandated by the Ogg
    bitstream spec. The second header holds any comment fields. The
    third header holds the bitstream codebook. We merely need to
    make the headers, then pass them to libvorbis one at a time;
    libvorbis handles the additional Ogg bitstream constraints */

    {
    ogg_packet header;
    ogg_packet header_comm;
    ogg_packet header_code;

    vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code);
    ogg_stream_packetin(&os, &header); /* automatically placed in its own page */
    ogg_stream_packetin(&os, &header_comm);
    ogg_stream_packetin(&os, &header_code);

    /* This ensures the actual
    * audio data will start on a new page, as per spec
    */
    while (!eos)
    {
    int result = ogg_stream_flush(&os, &og);
    if (result == 0) break;

    [refToSelf appendToFile:AUDIORECOGG withBuffer:og.header withSize:og.header_len];
    [refToSelf appendToFile:AUDIORECOGG withBuffer:og.body withSize:og.body_len];
    }
    }

    return true;
    }

    void doOggVorbisEncoding(void *buffer, long bytes)
    {
    NSLog(@”doOggVorbisEncoding, bytes = %ld”, bytes);

    if (eos) return;

    // Size of vobis buffer (buffer to store the samples as float numbers)
    int READ = bytes / 2;
    unsigned char *readbuffer = (unsigned char *)buffer;

    if (bytes == 0)
    {
    /* end of file. this can be done implicitly in the mainline,
    but it’s easier to see here in non-clever fashion.
    Tell the library we’re at end of stream so that it can handle
    the last frame and mark end of stream in the output properly */
    vorbis_analysis_wrote(&vd, 0);

    NSLog(@”doOggVorbisEncoding Zero Bytes”);
    }
    else
    {
    /* data to encode */
    NSLog(@”doOggVorbisEncoding Data Encode”);

    /* expose the buffer to submit data */
    float **buffer = vorbis_analysis_buffer(&vd, READ);
    long i;

    /* uninterleave samples */
    for (i = 0 ; i < bytes / 2 ; i++)
    {
    // Convert 2 bytes to signed int16 (big endian)
    int16_t sample = ((0xff00 & (readbuffer[i*2] << 8)) | (0x00ff & readbuffer[i*2+1]));

    // Normalize to 1
    buffer[0][i] = sample / 32768.f;
    }

    /* tell the library how much we actually submitted */
    vorbis_analysis_wrote(&vd, i);
    }

    /* vorbis does some data preanalysis, then divvies up blocks for
    more involved (potentially parallel) processing. Get a single
    block for encoding now */
    while (vorbis_analysis_blockout(&vd, &vb) == 1)
    {
    /* analysis, assume we want to use bitrate management */
    vorbis_analysis(&vb, NULL);
    vorbis_bitrate_addblock(&vb);

    while (vorbis_bitrate_flushpacket(&vd, &op))
    {
    /* weld the packet into the bitstream */
    ogg_stream_packetin(&os, &op);

    /* write out pages (if any) */
    while (!eos)
    {
    int result = ogg_stream_pageout(&os, &og);

    if (result == 0) break;

    [refToSelf appendToFile:AUDIORECOGG withBuffer:og.header withSize:og.header_len];
    [refToSelf appendToFile:AUDIORECOGG withBuffer:og.body withSize:og.body_len];

    /* this could be set above, but for illustrative purposes, I do
    it here (to show that vorbis does know where the stream ends) */

    if (ogg_page_eos(&og))
    {
    NSLog(@"End Of Stream");
    eos = 1;
    }
    }
    }
    }
    }

    void closeOggVorbisEncoder()
    {
    NSLog(@"closeOggVorbisEncoder");

    /* clean up and exit. vorbis_info_clear() must be called last */
    ogg_stream_clear(&os);
    vorbis_block_clear(&vb);
    vorbis_dsp_clear(&vd);
    vorbis_comment_clear(&vc);
    vorbis_info_clear(&vi);
    }

    @end

  2. satish says:

    Hi Idz.

    We might have some project for you. Please contact me as earliest!.

    Thanks!

  3. Good afternoon,

    I wanted to thank you for the thorough, well written review of the OWC Aura Pro SSD Envoy Upgrade Kit for MacBook Air, that ran 10-14-14. I’d also like permission to run the review on the product pages and on our Press room.

    I did have one comment about this line of the article:

    “You should be aware that Apple does not support upgrading the SSD drive and what is described in this article will (most likely) void your warranty.”

    There is a lot of misinformation/misconception on Apple warranties. There is a piece of federal legislation called the Magnusson-Moss Act, which provides consumer protections. In this case, the manufacturer has the burden of proof that the addition of a modification results in the failure of other components.

    EX: Apple couldn’t claim a computer screen failed because a hard drive was upgraded …and if they did, they would have to prove such.
    The only thing they could say wasn’t covered is the actual hard drive bay — if someone puts a hard drive in wrong and damaged the bay, that would not be covered under warranty.

    In the same example, an additional reason Apple would not jump in to void a warranty when a customer upgrades a hard drive is that the Apple site itself offers directions on how to do that, as is seen in this sample link:

    http://support.apple.com/manuals/#install%20hard%20drive

    In the eight years I have been here, I have not heard of one OWC customer being denied warranty coverage by the specific act of installing memory, drive, etc, into their machine.

    I look forward to hearing back from you, about permission for us to run the review. And again, thank you for a great review!

    Eileen Millard
    OWC Public Relations Manager
    eileen@macsales.com
    815-333-5023

  4. verde339 says:

    Can you guys make a new tutorial on building a web browser for iOS 8. I followed your building a web browser tutorial and even the revised one for iOS 7 :D I loved it and it’s a GREAT way to learn!! I am very glad I found this site. I read that iOS 8 uses a new WebKit UI or something and it’s suppose to be faster. (They also added iPhone 6 & 6+) support as you probably know. Also, I would love to make a simple iPad browser too! I was thinking about making a browser without an address bar and it could be used in stores and such so it’s always on that webpage.

Leave a Reply