# Action Table - Audio

## Introduction

This tutorial will guide you through using the JamAI Base TypeScript SDK to create a simple audio information processing system.

What We'll Build

Automate tasks with audio! Imagine processing meeting recordings or voice memos automatically.

In this tutorial, we'll create an audio processing system that:

* Takes an audio file as input (e.g., `.mp3`, `.wav`).
* Uploads it to a JamAI Base action table.
* Extracts and generates key information such as:
  * Transcription of the audio content.
  * Summary of the audio.

Prerequisites

Before starting, you'll need:

* Node.js 16.x or higher installed
* Project ID and Personal Access Token (PAT)

## Installation and Setup

Installing the SDK

```bash
npm install jamaibase
```

Basic Configuration

Get your Personal Access Token (PAT) here:

Get your Project ID here:

```typescript
import JamAI from "jamaibase";

const PROJECT_ID = "your_project_id";
const PAT = "your_PAT";

const client = new JamAI({
  projectId: PROJECT_ID,
  token: PAT,
});
```

## Creating Your Action Table

{% stepper %}
{% step %}

### Create the action table

Navigate to your JamAI Base action table tab and create a new action table named "AudioProcessor".
{% endstep %}

{% step %}

### Configure columns

Configure the following columns:

Input Column:

* Name: "Audio"
* Type: Audio

Output Columns:

* Name: "Transcription" Type: Text ☑️ Output checkbox enabled
* Name: "Summary" Type: Text ☑️ Output checkbox enabled
  {% endstep %}

{% step %}

### Verify table

Create and confirm the table:
{% endstep %}
{% endstepper %}

## Basic Implementation

### 4.1 Simple Audio Processor

```typescript
async function processSingleAudio(audioPath: string) {
  // Upload audio file
  const fileResponse = await client.file.uploadFile({
    file_path: audioPath,
  });

  // Process in action table
  const response = await client.table.addRow({
    table_type: "action",
    table_id: "AudioProcessor",
    data: [{ Audio: fileResponse.uri }],
    concurrent: false,
  });

  // Extract results
  return {
    transcription:
      response.rows[0]?.columns["Transcription"]?.choices[0]?.message?.content,
    summary: response.rows[0]?.columns["Summary"]?.choices[0]?.message?.content,
  };
}
```

### 4.2 Complete Implementation with Error Handling

```typescript
import JamAI from "jamaibase";
import * as fs from "fs";
import * as path from "path";

class AudioProcessor {
  private client: JamAI;

  constructor(projectId: string, pat: string) {
    this.client = new JamAI({
      projectId: projectId,
      token: pat,
    });
  }

  validateAudio(audioPath: string): boolean {
    // Validate if file exists and has correct extension
    if (!fs.existsSync(audioPath)) {
      throw new Error(`Audio file not found: ${audioPath}`);
    }

    const validExtensions = [".mp3", ".wav"];
    const fileExt = path.extname(audioPath).toLowerCase();
    if (!validExtensions.includes(fileExt)) {
      throw new Error(
        `Unsupported file format. Use: ${validExtensions.join(", ")}`
      );
    }

    return true;
  }

  async processAudio(
    audioPath: string
  ): Promise<{ transcription: string; summary: string } | null> {
    try {
      // Validate audio
      this.validateAudio(audioPath);

      // Upload file
      console.log("Uploading audio...");
      const fileResponse = await this.client.file.uploadFile({
        file_path: audioPath,
      });
      console.log(`Upload successful: ${fileResponse.uri}`);

      // Process in action table
      console.log("Processing audio...");
      const response = await this.client.table.addRow({
        table_type: "action",
        table_id: "AudioProcessor",
        data: [{ Audio: fileResponse.uri }],
        concurrent: false,
      });

      const results = {
        transcription: String(
          response.rows[0]?.columns["Transcription"]?.choices[0]?.message
            ?.content ?? ""
        ),
        summary: String(
          response.rows[0]?.columns["Summary"]?.choices[0]?.message?.content ??
            ""
        ),
      };

      console.log("Processing complete!");
      return results;
    } catch (error) {
      console.error(`Error processing audio: ${error}`);
      return null;
    }
  }
}
```

## Usage Examples

### Basic Usage

```typescript
// Initialize processor
const processor = new AudioProcessor(PROJECT_ID, PAT);

// Process single audio
const result = await processor.processAudio("path/to/audio.mp3");

if (result) {
  console.log(`Transcription: ${result.transcription}`);
  console.log(`Summary: ${result.summary}`);
}
```

### Batch Processing

```typescript
async function processAudioBatch(audioFolder: string) {
  const processor = new AudioProcessor(PROJECT_ID, PAT);
  const results = [];

  const files = fs.readdirSync(audioFolder);

  for (const filename of files) {
    if (filename.toLowerCase().match(/\.(mp3|wav)$/)) {
      const audioPath = path.join(audioFolder, filename);
      const result = await processor.processAudio(audioPath);
      if (result) {
        results.push({
          filename: filename,
          ...result,
        });
      }
    }
  }

  return results;
}

// Usage
const results = await processAudioBatch("path/to/audio/folder");
for (const result of results) {
  console.log(`File: ${result.filename}`);
  console.log(`Transcription: ${result.transcription}`);
  console.log(`Summary: ${result.summary}`);
  console.log("---");
}
```

## Best Practices

Error Handling

* Always validate input audio files.
* Handle network errors gracefully.
* Consider adding specific error handling for audio processing failures (e.g., silence, corrupted files).

Performance

* Reuse the client instance.
* Consider batch processing for multiple files.
* Implement rate limiting for large batches if needed.

Security

* Use environment variables for credentials.

Complete Standalone Example

Save this as `audio_processor.ts`:

```typescript
import JamAI from "jamaibase";
import * as fs from "fs";
import * as path from "path";

class AudioProcessor {
  private client: JamAI;

  constructor(projectId: string, pat: string) {
    this.client = new JamAI({
      projectId: projectId,
      token: pat,
    });
  }

  validateAudio(audioPath: string): boolean {
    if (!fs.existsSync(audioPath)) {
      throw new Error(`Audio file not found: ${audioPath}`);
    }

    const validExtensions = [".mp3", ".wav"];
    const fileExt = path.extname(audioPath).toLowerCase();
    if (!validExtensions.includes(fileExt)) {
      throw new Error(
        `Unsupported file format. Use: ${validExtensions.join(", ")}`
      );
    }

    return true;
  }

  async processAudio(
    audioPath: string
  ): Promise<{ transcription: string; summary: string } | null> {
    try {
      this.validateAudio(audioPath);

      console.log(`Processing audio: ${audioPath}`);
      console.log("Uploading audio...");
      const fileResponse = await this.client.file.uploadFile({
        file_path: audioPath,
      });
      console.log("Upload successful!");

      console.log("Extracting information...");
      const response = await this.client.table.addRow({
        table_type: "action",
        table_id: "AudioProcessor",
        data: [{ Audio: fileResponse.uri }],
        concurrent: false,
      });

      const results = {
        transcription: String(
          response.rows[0]?.columns["Transcription"]?.choices[0]?.message
            ?.content ?? ""
        ),
        summary: String(
          response.rows[0]?.columns["Summary"]?.choices[0]?.message?.content ??
            ""
        ),
      };
      return results;
    } catch (error) {
      console.error(`Error: ${error}`);
      return null;
    }
  }
}

async function processFolder(
  folderPath: string,
  processor: AudioProcessor
): Promise<void> {
  // Process all audio files in a folder
  if (!fs.existsSync(folderPath)) {
    console.log(`Folder not found: ${folderPath}`);
    return;
  }

  const results = [];
  const files = fs.readdirSync(folderPath);

  for (const filename of files) {
    if (filename.toLowerCase().match(/\.(mp3|wav)$/)) {
      const audioPath = path.join(folderPath, filename);
      const result = await processor.processAudio(audioPath);
      if (result) {
        results.push({
          filename: filename,
          ...result,
        });
      }
    }
  }

  console.log("\n=== Processing Results ===");
  for (const result of results) {
    console.log(`\nFile: ${result.filename}`);
    console.log(`Transcription: ${result.transcription}`);
    console.log(`Summary: ${result.summary}`);
  }
}

// Main execution
async function main() {
  // Get credentials from environment variables
  const PROJECT_ID = "your_project_id";
  const PAT = "your_PAT";

  const processor = new AudioProcessor(PROJECT_ID, PAT);

  // Process a single audio file
  const singleResult = await processor.processAudio("path/to/audio.mp3");
  if (singleResult) {
    console.log("\nSingle Audio Result:");
    console.log(`Transcription: ${singleResult.transcription}`);
    console.log(`Summary: ${singleResult.summary}`);
  }

  // Or process a folder of audio files
  // await processFolder("path/to/audio/folder", processor);
}

main().catch(console.error);
```

## Running the Example

1. Save the code as `audio_processor.ts`
2. Create a `.env` file:

   ```
   JAMAI_PROJECT_ID=your_project_id
   JAMAI_API_KEY=your_PAT
   ```
3. Install dependencies:

   ```bash
   npm install jamaibase dotenv
   npm install --save-dev @types/node typescript ts-node
   ```
4. Run with:

   ```bash
   ts-node audio_processor.ts
   ```

Or compile and run:

```bash
tsc audio_processor.ts
node audio_processor.js
```

## Troubleshooting

<details>

<summary>File not found error</summary>

* Check that the file path is correct
* Use absolute paths or ensure relative paths are correct

</details>

<details>

<summary>Unsupported file format</summary>

* Ensure audio is in MP3 or WAV format
* Check file extension matches actual file type

</details>

<details>

<summary>Upload timeout</summary>

* Check internet connection
* Verify file size is reasonable (< 25MB recommended)
* Large audio files may take longer to upload

</details>

<details>

<summary>Transcription errors</summary>

* Check audio quality (clear speech, minimal background noise)
* Ensure audio file is not corrupted
* Verify audio contains speech content

</details>

<details>

<summary>API errors</summary>

* Verify your PAT and Project ID are correct
* Check that the action table "AudioProcessor" exists in your project
* Ensure output columns "Transcription" and "Summary" are configured correctly

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.jamaibase.com/developer-reference/typescript-sdk-documentation/quick-start-action-table/action-table-audio.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
